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 | 5 | from django.utils.html import format_html |
| 6 | 6 | from django.utils.translation import gettext_lazy as _ |
| 7 | 7 | |
| 8 | +from .fields import JSONField | |
| 8 | 9 | from .abstracts import ( |
| 9 | 10 | COMMENT_MAX_LENGTH, BaseCommentAbstractModel, CommentAbstractModel, |
| 10 | 11 | ) |
| ... | ... | @@ -20,6 +21,12 @@ class Comment(CommentAbstractModel): |
| 20 | 21 | max_length=150, |
| 21 | 22 | ) |
| 22 | 23 | |
| 24 | + metadata = JSONField( | |
| 25 | + verbose_name=_('Metadata'), | |
| 26 | + null=True, | |
| 27 | + blank=True, | |
| 28 | + ) | |
| 29 | + | |
| 23 | 30 | class Meta(CommentAbstractModel.Meta): |
| 24 | 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> | |
| \ No newline at end of file | ... | ... |
Please
register
or
login
to post a comment