#29294: ModelAdmin.raw_id_fields should be included in select_related() by
change_view and used by raw id widget
-------------------------------------+-------------------------------------
Reporter: Yurii Zolot'ko | Owner: nobody
Type: | Status: new
Cleanup/optimization |
Component: contrib.admin | Version: 1.11
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jurrian Tromp):
* status: closed => new
* resolution: needsinfo =>
Comment:
I was about to create a new issue for what I think is the same thing so
reopening, I [https://stackoverflow.com/questions/65794839/using-
prefetch-related-in-django-in-combination-with-raw-id-fields also
encountered] this problem.
As stated the label_and_url_for_value() does a get() for each item. When
using the raw_id_fields, this is something you don't want. Especially
because it does not work with prefetching. I worked around the bug by
subclassing it in my own project and actually did a benchmark:
**Benchmark: change view page with 180 inline items with relations:**
1. Using no raw_id_field: 1658 queries
2. With raw_id_field and prefetching in Django 3.1: 384 queries
3. Patched raw_id_field and prefetching: 18 queries
This proves that my patched ForeignKeyRawIdWidget considerably reduces
queries as they already have been prefetched.
The culprit here is django.contrib.admin.widgets:176
{{{obj = self.rel.model._default_manager.using(self.db).get(**{key:
value})}}}
This is a excerpt of what is needed to improve the behaviour which can be
backwards compatible: we just need to try if value.pk exists, if not we
use the default behaviour:
{{{
class ForeignKeyRawIdWidget(widgets.ForeignKeyRawIdWidget):
def format_value(self, value):
"""Try to return the `pk` if value is an object, otherwise just
return
the value as fallback."""
if value == '' or value is None:
return None
try:
return str(value.pk)
except AttributeError:
return str(value)
def label_and_url_for_value(self, value):
"""Instead of the original we do not have do a `get()` anymore
instead
access the instance directly so when value is prefetched this will
prevent additional queries."""
try:
pk = value.pk
meta = value._meta
except AttributeError:
# Fallback for compatibility with plain pk values
return super().label_and_url_for_value(value)
}}}
Note that this is what I made in order to fix it in a project. If accepted
I can provide a more permanent solution. Please let me know if this of
interest.
--
Ticket URL: <https://code.djangoproject.com/ticket/29294#comment:2>
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/064.7cb0872790007e51b432e2e9e102a8d0%40djangoproject.com.