#35885: JSONField does accept strings that look like dicts and incorrectly saves
them as strings, breaking JSON filtering
---------------------------+-----------------------------------------
     Reporter:  DataGreed  |                     Type:  Uncategorized
       Status:  new        |                Component:  Uncategorized
      Version:  5.0        |                 Severity:  Normal
     Keywords:             |             Triage Stage:  Unreviewed
    Has patch:  0          |      Needs documentation:  0
  Needs tests:  0          |  Patch needs improvement:  0
Easy pickings:  0          |                    UI/UX:  0
---------------------------+-----------------------------------------
 Steps to reproduce:

 1. Create a model with JSONField, create a migration and migrate
 2. Create an instance of this model, set the value of JSON field to a
 string generated by `json.dumps()` from any dictionary, e.g.
 "{"foo":"bar"}
 3. Save the model instance
 4. Instance save successfully

 Expected result:

 Instance is saved with the JSONField set to a dictionary {"foo": "bar"},
 and JSONField deep filtering works properly

 Actual result:

 Instance is saved with a JSONField value saved as a string. Filtering does
 not work, and when you query this instance from a database, the JSONField
 value will be a string, not a dict.

 I tried it on PostgreSQL and sqlite - both working incorrectly.
 Note that if I set JSONField value to an actual dict it works correctly,
 but it is too easy to make a mistake and then waste hours of debugging the
 reasons the JSON filtering does not work, since developers usually
 sanitize the data at least by dumping the dict to json to make sure it is
 even possible to dump.

 It seems like the fix would be to parse the string value in JSONField
 before saving.
 My current workaround is to do the following on my models with JSONFields:



 {{{
 class SomeModel(models.Model):

     json_field = models.JSONField(blank=True, null=True)

     def clean(self):
         try:
             if isinstance(self.json_field, str):
                 # make sure we are not trying to save string to JSONField,
 django allows this for some reason
                 # and it breaks filtering on json
                 self.arguments = json.loads(self.json_field)
         except ValueError as e:
             # reraise as validation error. We do this to get more
 actionable description for the error
             raise ValidationError(str(e))

     def save(self, *args, **kwargs):

         # ensure clean runs. Well, kind of - we can still directly update
 the fields,
         # and it will somewhat break data integrity, so just don't do that
 maybe? thanks :)
         self.full_clean()
         super().save(*args, **kwargs)
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35885>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/01070192f808576f-76a131cc-fc11-4454-a129-ac7a891ec233-000000%40eu-central-1.amazonses.com.

Reply via email to