#35044: Accessing a deferred field clears reverse relations
-------------------------------------+-------------------------------------
Reporter: Adam | Owner: nobody
Johnson |
Type: | Status: assigned
Cleanup/optimization |
Component: Database | Version: dev
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
`DeferredAttribute.__get__` calls `Model.refresh_from_db()` to load the
deferred field. Whilst `refresh_from_db()` does load the intended field,
it also clears a lot of cached data, including reverse relations. This can
causes extra queries when those relations are accessed before and after a
deferred field.
For example, take these models:
{{{
class Book(models.Model):
title = models.TextField()
...
class BookText(models.Model):
book = models.OneToOneField(Book, on_delete=models.CASCADE)
text = models.TextField()
}}}
With query debug logging on, we can see that a second access to `booktext`
after accessing the deferred `title` causes an unnecessary extra query:
{{{
In [1]: from example.models import *
In [2]: b=Book.objects.defer('title').earliest('id')
(0.000) SELECT "example_book"."id", "example_book"."author_id" FROM
"example_book" ORDER BY "example_book"."id" ASC LIMIT 1; args=();
alias=default
In [3]: b.booktext
(0.000) SELECT "example_booktext"."id", "example_booktext"."book_id",
"example_booktext"."text" FROM "example_booktext" WHERE
"example_booktext"."book_id" = 1 LIMIT 21; args=(1,); alias=default
Out[3]: <BookText: BookText object (1)>
In [4]: b.title
(0.000) SELECT "example_book"."id", "example_book"."title" FROM
"example_book" WHERE "example_book"."id" = 1 LIMIT 21; args=(1,);
alias=default
Out[4]: 'A 1'
In [5]: b.booktext
(0.000) SELECT "example_booktext"."id", "example_booktext"."book_id",
"example_booktext"."text" FROM "example_booktext" WHERE
"example_booktext"."book_id" = 1 LIMIT 21; args=(1,); alias=default
Out[5]: <BookText: BookText object (1)>
}}}
This is due to `refresh_from_db()` clearing the reverse-related object
cache.
Spotted whilst working on #28586. My implementation for that will probably
fix this issue, but I thought it best to report this separately.
--
Ticket URL: <https://code.djangoproject.com/ticket/35044>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018c72b01ed0-9c2a8373-e543-4b90-9dea-e8059ad27b70-000000%40eu-central-1.amazonses.com.