#32332 closed Bug (fixed)
Saving parent object after setting on child leads to data loss for parents with non-numeric primary key.
Reported by: | Charlie DeTar | Owned by: | Hasan Ramezani |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 3.1 |
Severity: | Normal | Keywords: | |
Cc: | robinh00d, Jon Dufresne | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Given a model with a foreign key relation to another model that has a non-auto CharField as its primary key:
class Product(models.Model): sku = models.CharField(primary_key=True, max_length=50) class Order(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE)
If the relation is initialized on the parent with an empty instance that does not yet specify its primary key, and the primary key is subsequently defined, the parent does not "see" the primary key's change:
with transaction.atomic(): order = Order() order.product = Product() order.product.sku = "foo" order.product.save() order.save() assert Order.objects.filter(product_id="").exists() # Succeeds, but shouldn't assert Order.objects.filter(product=order.product).exists() # Fails
Instead of product_id
being populated with product.sku
, it is set to emptystring. The foreign key constraint which would enforce the existence of a product with sku=""
is deferred until the transaction commits. The transaction does correctly fail on commit with a ForeignKeyViolation
due to the non-existence of a product with emptystring as its primary key.
On the other hand, if the related unsaved instance is initialized with its primary key before assignment to the parent, it is persisted correctly:
with transaction.atomic(): order = Order() order.product = Product(sku="foo") order.product.save() order.save() assert Order.objects.filter(product=order.product).exists() # succeeds
Committing the transaction also succeeds.
This may have something to do with how the Order.product_id
field is handled at assignment, together with something about handling fetching of auto vs non-auto primary keys from the related instance.
Change History (8)
comment:1 by , 4 years ago
Description: | modified (diff) |
---|
comment:2 by , 4 years ago
Cc: | added |
---|---|
Summary: | Relation with non-auto CharField primary key is not correctly persisted if the primary key is defined after assignment → Saving parent object after setting on child leads to data loss for parents with non-numeric primary key. |
comment:3 by , 4 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:4 by , 4 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 4 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
Thanks for this report.
product_id
is an empty string in _prepare_related_fields_for_save() that's whypk
from a related object is not used. We could useempty_values
:but I'm not sure.
Related with #28147.