Django ORM: Neat (undocumented) trick
I just found out something pretty damn cool about Django’s ORM which, as it happens, is completely undocumented (as far as I can tell). Let’s assume your model definition is something like:
from django.db import models class MyModel(models.Model): count = models.IntegerField(default=0)
The following is completely valid, and actually eliminates a lot of the race conditions that have plagued the Django ORM in the past:
>>> from django.db.models import F >>> from myapp.models import MyModel >>> obj = MyModel(count=4) >>> obj.save() >>> obj.count 4 >>> obj.count = F('count') + 3 >>> obj.save() >>> obj = MyModel.objects.get(pk=obj.pk) # We need to reload the object. >>> obj.count 7
Typically you’d do something like
obj.count += 3, but that sets the attribute to an absolute value, which can be the cause of many a race condition wherein two threads/processes are editing the same record at a time; the
obj.save() would cause one thread to clobber another’s changes. Using
F(), the SQL expression instead looks like:
UPDATE "myapp_mymodel" SET "count" = "myapp_mymodel"."count" + 3 WHERE "myapp_mymodel"."id" = 1;
Here, the ACIDity of the RDBMS ensures that parallel attempts to increment the count occur without issue.
This behaviour’s undocumented status means it could break at any minute, and reloading the object is necessary because otherwise
obj.count ends up being an instance of
django.db.models.expressions.ExpressionNode, even after the object is saved.