#28806: Mechanism of fetching related objects violates READ COMMITTED
assumption of
Django ORM
-------------------------------------+-------------------------------------
Reporter: Aaron Tan | Owner: nobody
Type: Bug | Status: new
Component: Database | Version: master
layer (models, ORM) | Keywords: database, read-
Severity: Normal | committed, concurrency control
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
Found here:
https://github.com/django/django/blob/master/django/db/models/fields/related_descriptors.py#L141.
{{{
def get_object(self, instance):
qs = self.get_queryset(instance=instance)
# Assuming the database enforces foreign keys, this won't fail.
return qs.get(self.field.get_reverse_related_filter(instance))
}}}
The comment in that function states: `# Assuming the database enforces
foreign keys, this won't fail.`, but it's not the case. I will illustrate
with a use case we met:
Suppose we have 2 concurrent transactions A and B talking to the same
Postgresql backend, and we have two models:
{{{
class Foo:
pass
class Bar:
fk = ForeignKey(Foo, on_delete=models.SET_NULL)
}}}
populated by objects
{{{
foo = Foo()
foo.save()
bar = Bar(fk=foo)
bar.save()
}}}
Transaction A runs
{{{
bar = Bar.objects.get(pk=<bar's pk>)
}}}
Then transaction B runs
{{{
Foo.objects.get(pk=<foo's pk>).delete()
}}}
If READ COMMITTED is assumed by Django, transaction A will see the db
snapshot with `foo` deleted. But if transaction A subsequently did
{{{
bar.fk #Access foreign key field `fk` for the first time
}}}
Because the `get_object` method linked does not catch `ObjectDoesNotExist`
exception tossed by `get`, the last `bar.fk` will raise
`ObjectDoesNotExist`, thus contradicts the assumption
> the database enforces foreign keys, this won't fail.
We currently manually catch `ObjectDoesNotExist` but that makes code ugly,
I suggest instantiate the related field to `None` in that case, instead of
raising exception.
--
Ticket URL: <https://code.djangoproject.com/ticket/28806>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/052.eaa772e2bf7b6e528df1ebd625031b18%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.