2011-07-31 10 views
16

Uno de mis modelos es particularmente complejo. Cuando intento editarlo en Django Admin, realiza 1042 consultas y tarda más de 9 segundos en procesarse.¿Cómo forzar a Django Admin a usar select_related?

Sé que puedo reemplazar algunas de las listas desplegables con raw_id_fields, pero creo que el mayor cuello de botella es que no está realizando un select_related() como debería.

¿Puedo obtener el sitio de administración para hacer esto?

Respuesta

8

Para mi modelo en particular, el aspecto particularmente lento es pasar por ForeignKeys cuando se muestran en formularios, que no se llaman usando select_related, así que esa es la parte que voy a acelerar.

Mirando a través de la fuente de Django relevante, que se ve en django/contrib/admin/options.py que el método formfield_for_foreignkeys toma cada FK db_field y llama al método ForeignKey de clase formfield, que se define en django/db/modelos/campos/relacionados/como:

def formfield(self, **kwargs): 
    db = kwargs.pop('using', None) 
    defaults = { 
     'form_class': forms.ModelChoiceField, 
     'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to), 
     'to_field_name': self.rel.field_name, 
    } 
    defaults.update(kwargs) 
    return super(ForeignKey, self).formfield(**defaults) 

De esto, vemos si proporcionamos el db_field con un kwargs['queryset'] podemos definir un queryset personalizado que se utilizará select_related (esto puede ser proporcionado por formfield_for_foreignkey).

Así que básicamente lo que queremos hacer es anular admin.ModelAdmin con SelectRelatedModelAdmin y luego hacer que nuestros subclases ModelAdmin de SelectRelatedModelAdmin en lugar de admin.ModelAdmin

class SelectRelatedModelAdmin(admin.ModelAdmin): 
    def formfield_for_foreignkey(self, db_field, request, **kwargs): 
     if 'queryset' in kwargs: 
      kwargs['queryset'] = kwargs['queryset'].select_related() 
     else: 
      db = kwargs.pop('using', None) 
      kwargs['queryset'] = db_field.rel.to._default_manager.using(db).complex_filter(db_field.rel.limit_choices_to).select_related() 
     return super(SelectRelatedModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) 

este ejemplo de código no cubre administrador Inline s o s ManyToManyField o foreign_key recorrido en las funciones llamadas por readonly_fields o consultas personalizadas de selección, pero un enfoque similar debería funcionar para esos casos.

+0

Esto podría simplificarse 'get_field_queryset' (aunque es indocumentado y por lo tanto se podría cambiar en el futuro). –

25

Aunque la respuesta de dr jimbob tiene sentido, para mis necesidades, pude simplemente anular el método get_queryset() con un único liner, incluso seleccionando la clave externa de una clave foránea. Tal vez esto podría ser útil para alguien.

class MyModelAdmin(admin.ModelAdmin): 
    model = MyModel 
    ... 
    def get_queryset(self, request): 
     return super(MyModelAdmin, self).get_queryset(request).select_related(
      'foreign_key1', 'foreign_key2__fk2_foreign_key') 
+1

queryset ha sido renombrado a [get_queryset] (https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_queryset) – phoibos

+0

no parece que esto funcione en la página ** cambio **. Es decir, el formulario todavía está haciendo miles de consultas para obtener instancias relacionadas. ¿Soy solo yo, o esto funciona para otros? django == 1.11.3 –

16

puede probar esta

class Foo(admin.ModelAdmin): 
    list_select_related = (
     'foreign_key1', 
     'foreign_key2', 
    ) 

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_select_related

+2

Lamentablemente, no creo que esto resuelva la consulta original. El atributo 'list_select_related' es para la página Admin _listing_ y no para el objeto _edit_ page. Ciertamente, en mi experiencia con este mismo problema, tener un conjunto relacionado con la lista no acelera la página de edición, solo la lista y la página de selección. –