id,summary,reporter,owner,description,type,status,component,version,severity,resolution,keywords,cc,stage,has_patch,needs_docs,needs_tests,needs_better_patch,easy,ui_ux 28806,Mechanism of fetching related objects violates READ COMMITTED assumption of Django ORM,Aaron Tan,nobody,"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=) }}} Then transaction B runs {{{ Foo.objects.get(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.",Bug,closed,"Database layer (models, ORM)",dev,Normal,wontfix,"database, read-committed, concurrency control",,Unreviewed,0,0,0,0,0,0