#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 django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.7cb0872790007e51b432e2e9e102a8d0%40djangoproject.com.

Reply via email to