#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.

Reply via email to