Eager to hear advice about a scenario I encountered in which I felt the ORM didn’t live up to the maxim that “Django abstracts the need to use INSERT
or UPDATE
SQL statements.”
Example: Create data, then update that data without a model instance in hand.
class WithoutDefault(models.Model):
name = models.CharField(primary_key=True)
message = models.CharField(null=True)
In [1]: from my_app.models import WithoutDefault
In [2]: snow = WithoutDefault(name="snow")
In [3]: snow.save() # imagine this instance goes away
In [4]: remade_snow = WithoutDefault(name="snow", message="fell")
In [5]: remade_snow.save()
In [6]: remade_snow.message
Out[6]: 'fell'
Same thing, but the primary key has a default.
class WithDefault(models.Model):
name = models.CharField(primary_key=True, default="unrealistic default")
message = models.CharField(null=True)
In [1]: from my_app.models import WithDefault
In [2]: snow = WithDefault(name="snow")
In [3]: snow.save() # imagine this instance goes away
In [4]: remade_snow = WithDefault(name="snow", message="fell")
In [5]: remade_snow.save()
---------------------------------------------------------------------------
UniqueViolation Traceback (most recent call last)
UniqueViolation: duplicate key value violates unique constraint "myapp_withdefault_pkey"
DETAIL: Key (name)=(snow) already exists.
The above exception was the direct cause of the following exception:
IntegrityError: duplicate key value violates unique constraint "myapp_withdefault_pkey"
DETAIL: Key (name)=(snow) already exists.
Debugging narrative
- Aha, I must need an UPDATE. But isn’t the ORM supposed to abstract that away?
- Try
force_update=True
(doesn’t do the trick) - Try
force_update=True, update_fields=None
, (doesn’t do the trick) - Review the docs for save():
Django executes an
UPDATE
if it is an existing model instance and primary key is set to a value that exists in the database.
- Didn’t grok “existing model instance” at first. I constructed an instance, and the value exists in the database. Does the instance exist?
- See the discussion about
select_on_save()
(doesn’t do the trick) - Issue
WithDefault.objects.filter(pk="snow").delete()
before the save and carry on. (Or, I could fetch the instance and then assign attributes to it.)
Impressions
- I was in an “update_or_create”-like scenario, where I didn’t know or care whether “snow” existed before. In fact, this works fine:
In [11]: WithDefault.objects.update_or_create(name="snow", defaults={"message": "fell"})
Out[11]: (<WithDefault: WithDefault object (snow)>, False)
- But I was working with a model with over a dozen attributes. Shoving them all into “defaults” just to update one row felt a little funny–especially after I debugged my program and realized I was in a scenario where I would always be updating.
- I really just wanted to be able to construct an instance and
save()
, and the only reason it didn’t work was that the primary key field had a default, which struck me as an orthogonal detail.
Questions
- Should the ORM have attempted an UPDATE instead here?
- If not, should
force_update=True
have at least worked? - How do folks feel about a reword of “existing” model instance to “fetched” model instance given that “exists” is used in two senses in the same sentence?
Notes
My more realistic default was a UUIDField with default=uuid.uuid4
; I just didn’t want to uglify my shell example.