Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.9 JSONField update behavior

I've recently updated to Django 1.9 and tried updating some of my model fields to use the built-in JSONField (I'm using PostgreSQL 9.4.5). As I was trying to create and update my object's fields, I came across something peculiar. Here is my model:

class Activity(models.Model):
    activity_id = models.CharField(max_length=MAX_URL_LENGTH, db_index=True, unique=True)
    my_data = JSONField(default=dict())

Here is an example of what I was doing:

>>> from proj import models
>>> test, created = models.Activity.objects.get_or_create(activity_id="foo")
>>> created
True
>>> test.my_data['id'] = "foo"
>>> test.save()
>>> test
<Activity: {"id": "foo"}>
>>> test2, created2 = models.Activity.objects.get_or_create(activity_id="bar")
>>> created2
True
>>> test2
<Activity: {"id": "foo"}>
>>> test2.activity_id
'bar'
>>> test.activity_id
'foo'

It seems whenever I update any field in my_data, the next object I create pre-populates itself with the data from my_data from the previous object. This happens whether I use get_or_create or just create. Can someone explain to me what is happening?

like image 771
user994013 Avatar asked Feb 18 '16 13:02

user994013


Video Answer


1 Answers

The problem is that you are using default=dict(). Python dictionaries are mutable. The default dictionary is created once when the models file is loaded. After that, any changes to instance.my_data alter the same instance, if they are using the default value.

The solution is to use the callable dict as the default instead of dict().

class Activity(models.Model):
    my_data = JSONField(default=dict)

The JSONField docs warn about this:

If you give the field a default, ensure it’s a callable such as dict (for an empty default) or a callable that returns a dict (such as a function). Incorrectly using default={} creates a mutable default that is shared between all instances of JSONField.

like image 85
Alasdair Avatar answered Oct 04 '22 00:10

Alasdair