Commit aeb24f7c4582aac5af874008f9b0b852d1670039
1 parent
0751312e
[Add] Add JSONField and JsonFormWidget for handling JSON data in comments
- Introduced JSONField to store JSON metadata in the Comment model. - Added JsonFormWidget for rendering a JSON editor in forms. - Created a migration to add the new metadata field to the Comment model. - Included a template for the JSON editor widget.
Showing
4 changed files
with
113 additions
and
0 deletions
django_comments/fields.py
0 → 100644
| 1 | + | ||
| 2 | +from django import forms | ||
| 3 | +from django.core.serializers.json import DjangoJSONEncoder | ||
| 4 | +from django.db import models | ||
| 5 | +from django.db.backends.base.base import BaseDatabaseWrapper | ||
| 6 | + | ||
| 7 | +from simplejson import JSONDecodeError | ||
| 8 | +from typing import Any, Dict, Optional, Union | ||
| 9 | +import simplejson | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +__all__ = ['JSONField', 'JsonFormWidget'] | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +class JsonFormWidget(forms.Textarea): | ||
| 16 | + template_name = 'comments/json_field_widget.html' | ||
| 17 | + | ||
| 18 | + def get_context(self, name: str, value: Any, attrs: Optional[Dict]) -> Dict[str, Any]: | ||
| 19 | + ctx = super().get_context(name, value, attrs) | ||
| 20 | + ctx['widget']['json_value'] = simplejson.dumps(value) | ||
| 21 | + return ctx | ||
| 22 | + | ||
| 23 | + class Media: | ||
| 24 | + js = [ | ||
| 25 | + 'https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/8.6.3/jsoneditor.js', | ||
| 26 | + 'base_model_s/widgets/json_editor{min}.js', | ||
| 27 | + ] | ||
| 28 | + css = { | ||
| 29 | + 'all': [ | ||
| 30 | + 'https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/8.6.3/jsoneditor.min.css', | ||
| 31 | + ] | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +class JSONField(models.TextField): | ||
| 36 | + | ||
| 37 | + def formfield(self, **kwargs: Any) -> forms.Field: | ||
| 38 | + kwargs.setdefault('widget', JsonFormWidget) | ||
| 39 | + return super().formfield(**kwargs) | ||
| 40 | + | ||
| 41 | + @staticmethod | ||
| 42 | + def _get_json_value(value: str) -> Optional[Union[dict, list]]: | ||
| 43 | + | ||
| 44 | + if not value: | ||
| 45 | + return None | ||
| 46 | + | ||
| 47 | + if isinstance(value, list): | ||
| 48 | + return value | ||
| 49 | + | ||
| 50 | + try: | ||
| 51 | + return simplejson.loads(value) | ||
| 52 | + except JSONDecodeError: | ||
| 53 | + return {'data': str(value)} | ||
| 54 | + | ||
| 55 | + def from_db_value(self, value: Any, expression, connection: BaseDatabaseWrapper) -> Any: | ||
| 56 | + return self._get_json_value(value) | ||
| 57 | + | ||
| 58 | + def pre_save(self, model_instance: models.Model, add: bool) -> str: | ||
| 59 | + """ Convert Python to Json format before save in database """ | ||
| 60 | + | ||
| 61 | + value = super().pre_save(model_instance, add) | ||
| 62 | + if isinstance(value, str): | ||
| 63 | + return value | ||
| 64 | + | ||
| 65 | + return simplejson.dumps(value, cls=DjangoJSONEncoder) | ||
| 66 | + | ||
| 67 | + def clean(self, value, model_instance): | ||
| 68 | + """ | ||
| 69 | + Convert the value's type and run validation. Validation errors | ||
| 70 | + from _get_json_value() and validate() are propagated. Return the correct | ||
| 71 | + value if no error is raised. | ||
| 72 | + """ | ||
| 73 | + value = self._get_json_value(value) | ||
| 74 | + | ||
| 75 | + self.validate(value, model_instance) | ||
| 76 | + self.run_validators(value) | ||
| 77 | + | ||
| 78 | + return value |
| 1 | +from django.db import migrations | ||
| 2 | +import django_comments.fields | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +class Migration(migrations.Migration): | ||
| 6 | + | ||
| 7 | + dependencies = [ | ||
| 8 | + ('django_comments', '0006_auto_20200830_1322'), | ||
| 9 | + ] | ||
| 10 | + | ||
| 11 | + operations = [ | ||
| 12 | + migrations.AddField( | ||
| 13 | + model_name='comment', | ||
| 14 | + name='metadata', | ||
| 15 | + field=django_comments.fields.JSONField( | ||
| 16 | + blank=True, | ||
| 17 | + null=True, | ||
| 18 | + verbose_name='Metadata', | ||
| 19 | + ), | ||
| 20 | + ), | ||
| 21 | + ] |
| @@ -5,6 +5,7 @@ from django.utils import timezone | @@ -5,6 +5,7 @@ from django.utils import timezone | ||
| 5 | from django.utils.html import format_html | 5 | from django.utils.html import format_html |
| 6 | from django.utils.translation import gettext_lazy as _ | 6 | from django.utils.translation import gettext_lazy as _ |
| 7 | 7 | ||
| 8 | +from .fields import JSONField | ||
| 8 | from .abstracts import ( | 9 | from .abstracts import ( |
| 9 | COMMENT_MAX_LENGTH, BaseCommentAbstractModel, CommentAbstractModel, | 10 | COMMENT_MAX_LENGTH, BaseCommentAbstractModel, CommentAbstractModel, |
| 10 | ) | 11 | ) |
| @@ -20,6 +21,12 @@ class Comment(CommentAbstractModel): | @@ -20,6 +21,12 @@ class Comment(CommentAbstractModel): | ||
| 20 | max_length=150, | 21 | max_length=150, |
| 21 | ) | 22 | ) |
| 22 | 23 | ||
| 24 | + metadata = JSONField( | ||
| 25 | + verbose_name=_('Metadata'), | ||
| 26 | + null=True, | ||
| 27 | + blank=True, | ||
| 28 | + ) | ||
| 29 | + | ||
| 23 | class Meta(CommentAbstractModel.Meta): | 30 | class Meta(CommentAbstractModel.Meta): |
| 24 | db_table = "django_comments" | 31 | db_table = "django_comments" |
| 25 | 32 |
| 1 | +<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} style="display: none"> | ||
| 2 | + {% if widget.value %}{{ widget.value }}{% endif %}</textarea> | ||
| 3 | + <div class="json-editor" style="height: 700px" | ||
| 4 | + id="json-editor_{{ widget.attrs.id }}" | ||
| 5 | + data-value="{{ widget.json_value|default:''|force_escape }}" | ||
| 6 | + data-id="{{ widget.attrs.id }}" | ||
| 7 | + ></div> |
Please
register
or
login
to post a comment