25

in my model defn i have

from django.contrib.postgres.fields import JSONField
.....
.......
.........

media_data=JSONField(default=dict)

I created a default admin

When i attempt to save without touching the field, i get a this field is required error.

field is required

It looks like a form validation issue because I can programatically save the model instance from code without issue.

Why is this happening?
have i missed something silly?

w--
  • 6,427
  • 12
  • 54
  • 92
  • 7
    No, it's indeed a validation problem. For mysterious reasons, {} is not considered "valid json". You have to add `blank=True`. – Endre Both Mar 14 '19 at 10:03
  • much grr. thank you for the confirmation. is there docs somewhere about this? – w-- Mar 14 '19 at 14:04
  • You might want to document it yourself in Django's [ticket system](https://code.djangoproject.com/) :). – Endre Both Mar 14 '19 at 14:09
  • The docs , the meaning of blank=True in terms of validation: https://docs.djangoproject.com/en/4.2/ref/models/fields/#blank – KenBuckley Feb 07 '23 at 12:22

7 Answers7

31
  1. What happening. when dive into the source code. we can see the following call stack:
    1) form.is_valid() 
       ->form.full_clean()
        -->form._clean_fields()
         ---> self.cleand_data[name] = field.clean(value)
    2) field.clean(value)
        -> self.to_python(value)
        -> self.validate(value)

when look into the source code ,you can find that,it's mainly because the empty_values check.

# These values, if given to validate(), will trigger the self.required check.
EMPTY_VALUES = (None, '', [], (), {})

as you can see the empty dict {} is as an empty value for JSONField. so it will raise Error.

  1. What can we do? the Solution would be to customize the models.JSONField and forms.JSONField like below.

forms.py

from django.contrib.postgres import forms

class MyJSONField(forms.JSONField):
    empty_values = [None, "", [], ()]

db/fields.py

class MyJSONField(JSONField):
    def formfield(self, **kwargs):
        from ..forms import MyJSONField

        return super().formfield(**{"form_class": MyJSONField, **kwargs})
banxi1988
  • 1,322
  • 13
  • 10
  • 1
    great investigation! thanks. If you are interested, you may want to consider submiting a PR to the Django codebase for this. Per your discovery above, the empty_values check should be different for forms.JSONField. – w-- May 24 '19 at 17:31
  • 2
    This nearly worked for me with `django-mysql`. I just had to add `empty_values = [None, "", [], ()]` to the `MyJSONField` defined in fields.py _as well as_ the one defined in forms.py. (When I'd only modified the one in forms.py, entering '{}' gave me a 'This field cannot be blank.' error, instead of a 'This field is required' error.) – tessafyi Aug 19 '20 at 00:26
  • 1
    this was raised as a bug with a PR in [2016](https://code.djangoproject.com/ticket/26391) but was closed due to missing tests – Matthew Hegarty Jan 05 '21 at 18:47
  • 1
    It is discussed in [this issue](https://code.djangoproject.com/ticket/27697) - there is a patch but it needs improvement – Matthew Hegarty Jan 06 '21 at 10:39
7

I faced the same issue. How did I fix it? In order to bypass the validation process that excludes {} as valid json, I added both blank=True and null=True. For example: models.JSONField(blank=True, null=True, default=dict)

2

Depending on your requirements, consider to use blank and/or null.

media_data=JSONField(blank=True, null=True)

Ruben
  • 129
  • 5
  • yeah but i don't want blank, i want by default an empty dict/ JSON object, which is fundamentally different from a blank or null value. – w-- Mar 14 '19 at 14:05
  • @w-- The blank=True , relates to the validation, not to blank values. So it will tell validation to allow the EMPTY_VALUES when validating (say a full_clean). see here for the explanation: https://docs.djangoproject.com/en/4.2/ref/models/fields/#blank . Combine this with a default=dict in the model definition (or default=list) or default=None, as you wish. – KenBuckley Feb 07 '23 at 12:18
2

This caused problems for me recently, though with django-mysql rather than postgres and in a custom ModelForm rather than the admin interface.

I ended up overriding my model's save() method:

from django_mysql.models import JSONField

class yourModel(model):
    media_data=JSONField(default=dict, blank=True)

    def clean(self, *args, **kwargs):
        if self.media_data is None:
            self.media_data = "{}"

    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)
tessafyi
  • 2,273
  • 2
  • 19
  • 28
1

This works in django 4.1 admin screen:

class YourModel(Model):
    your_field = JSONField(default=list, null=False, blank=True)

I can leave the text field as [] and the admin screen saves it fine.

nurettin
  • 11,090
  • 5
  • 65
  • 85
0

i have similar problem previously. adding required=False in your form field will solve the problem.

class YourForm(forms.ModelForm):
    media_data = SimpleArrayField(JSONField(), required=False, widget=forms.Textarea, delimiter='|')
0

I had the same issue and similarly I created a new Field based class but instead of using a form and model field I just made one that tweaked the innards of the formfield after it gets instantiated.

class CustomJSONField(JSONField):
empty_values = [None, '', [], ()]

def formfield(self, **kwargs):
    result = super().formfield(**kwargs)
    result.empty_values = self.empty_values
    return result