#31071 closed Bug (fixed)
Change in behaviour when saving a model instance with an explcit pk value if the pk field has a default
| Reported by: | Reupen Shah | Owned by: | Simon Charette |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 3.0 |
| Severity: | Release blocker | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Consider the following model:
from uuid import uuid4
from django.db import models
class Sample(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
name = models.CharField(blank=True, max_length=100)
In Django 2.2 and earlier, the following commands would result in an INSERT followed by an UPDATE:
s0 = Sample.objects.create() s1 = Sample(pk=s0.pk, name='Test 1') s1.save()
However, in Django 3.0, this results in two INSERTs (naturally the second one fails). The behaviour also changes if default=uuid4 is removed from the id field.
This seems related to https://code.djangoproject.com/ticket/29260.
The change in behaviour also has the side effect of changing the behaviour of the loaddata management command when the fixture contains explicit pk values and the objects already exist (e.g. when loading the fixture multiple times).
Perhaps the intention was to only change the behaviour if an explicit pk value was not set on the model instance being saved? (At least, that would be more backwards-compatible behaviour...)
Change History (11)
comment:1 by , 6 years ago
| Description: | modified (diff) |
|---|
comment:2 by , 6 years ago
| Description: | modified (diff) |
|---|
comment:3 by , 6 years ago
| Severity: | Normal → Release blocker |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:4 by , 6 years ago
I bisected the regression down to 85458e94e38c20e57939947ee515a1a53689659f if that helps.
comment:5 by , 6 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
follow-up: 8 comment:6 by , 6 years ago
I'm afraid we'll have to revert 85458e94e38c20e57939947ee515a1a53689659f if we want this pattern to work because we assign field defaults on Model.__init__ without tracking whether or not they were generated from field defaults or not which makes both Sample() and Sample(pk=s0.pk) have pk_set=True in _save_table.
Note that this limitation was mentioned in https://code.djangoproject.com/ticket/29260#comment:3 so another option could be to document that force_update must be used in this particular case. I feel like this would be good compromise. Regarding the fixture loading we should branch of raw which is passed by the serialization framework to disable the optimiation.
Happy to provide a patch for whatever solution we choose. I think it's worth adjusting the feature given it does reduce the number of queries significantly when using primary key defaults and documenting it as a backward incompatible change that can be worked around by passing force_update but simply reverting the feature and reopening #29260 to target the next release is likely less trouble.
comment:7 by , 6 years ago
If it helps, I noticed this through the changed behaviour of loaddata in such cases (rather than the example code given). (I don't think we have any code that directly looks like the example.)
comment:8 by , 6 years ago
Replying to Simon Charette:
Note that this limitation was mentioned in https://code.djangoproject.com/ticket/29260#comment:3 so another option could be to document that
force_updatemust be used in this particular case. I feel like this would be good compromise. Regarding the fixture loading we should branch ofrawwhich is passed by the serialization framework to disable the optimiation.
Happy to provide a patch for whatever solution we choose. I think it's worth adjusting the feature given it does reduce the number of queries significantly when using primary key defaults and documenting it as a backward incompatible change that can be worked around by passing force_update...
I really like this proposition and I think it's fine to adjust the current fix and backport it to the Django 3.0.
It looks like the logic in
_save_tableshould not force an insert if an explicitpkvalue is provided.The logic should likely take
pk_setinto accountI'm surprised this was not caught by the suite if this breaks fixtures loading.