Commit aeb24f7c4582aac5af874008f9b0b852d1670039

Authored by Alejandro Ortiz
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.
  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