简体   繁体   中英

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?

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 .

from django_postgres_extensions.models.expressions import Key
obj = Product.objectsannotate(Key('description', 'Details')).get()
obj = Product.objects.annotate(Key('description', 'Details__Rating')).get()
obj = Product.objects.annotate(Key('description', 'Tags__1')).get()

Product.objects.update(description__ = {'Industry': 'Movie', 'Popularity': 'Very Popular'})

Product.objects.update(description__del ='Details')
Product.objects.update(description__del = 'Details__Release')
Product.objects.update(description__del='Tags__1')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM