54
you can try this
class Foo(admin.ModelAdmin):
list_select_related = (
'foreign_key1',
'foreign_key2',
)
45
Although dr jimbob’s answer makes sense, for my needs, I was able to simply override the get_queryset() method with a one-liner, even selecting a foreign key’s foreign key. Maybe this could be helpful to someone.
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')
- [Django]-Django: Catching Integrity Error and showing a customized message using template
- [Django]-How to translate a model label in Django Admin?
- [Django]-Explicitly set MySQL table storage engine using South and Django
14
For my particular model, the particularly slow aspect is going through ForeignKeys when they were being displayed in forms, which aren’t called using select_related
, so that’s the part I’m going to speed up.
Looking through the relevant django source, you see in django/contrib/admin/options.py
that the method formfield_for_foreignkeys
takes each FK db_field
and calls the ForeignKey
class’s formfield
method, which is defined in django/db/models/fields/related/ like:
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)
From this, we see if we provide the db_field
with a kwargs['queryset']
we can define a custom queryset that will be use select_related (this can be provided by formfield_for_foreignkey
).
So basically what we want to do is override admin.ModelAdmin
with SelectRelatedModelAdmin
and then make our ModelAdmin subclasses of SelectRelatedModelAdmin
instead of 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)
This code sample doesn’t cover admin Inline
s or ManyToManyField
s, or foreign_key traversal in functions called by readonly_fields
or custom select_related queries, but a similar approach should work for those cases.
- [Django]-Error trying to install Postgres for python (psycopg2)
- [Django]-How to express a One-To-Many relationship in Django?
- [Django]-How to use MySQLdb with Python and Django in OSX 10.6?
8
In Django 2.0+, a good way to improve performance of ForeignKey and ManyToMany relationships is to use autocomplete fields.
These fields don’t show all related objects and therefore load with many fewer queries.
- [Django]-How to check DEBUG true/false in django template – exactly in layout.html
- [Django]-Is there a HAML implementation for use with Python and Django
- [Django]-Create a field whose value is a calculation of other fields' values
7
For the admin edit/change a specific item page, foreign key select boxes may take a long time to load, to alter the way django queries the data for the foreign key:
Django docs on Using formfield_for_foreignkey
Say I have a field called foo
on my Example
model, and I wish to select ralated bar
objects:
class ExampleAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "foo":
kwargs["queryset"] = Example.objects.select_related('bar')
return super().formfield_for_foreignkey(db_field, request, **kwargs)
- [Django]-Creating email templates with Django
- [Django]-How to Unit test with different settings in Django?
- [Django]-"<Message: title>" needs to have a value for field "id" before this many-to-many relationship can be used.
3
For the sake of completeness, I would like to add another option that was the most suitable for my use case.
As others have pointed out, the problem is often loading the data for select boxes. list_select_related
does not help in this case.
In case you don’t actually want to edit the foreign key field via admin, the easiest fix is making the respective field readonly:
class Foo(admin.ModelAdmin):
readonly_fields = ('foreign_key_field1','foreign_key_field2',)
You can still display these fields, there will simply not be a select box, hence Django does not need to retrieve all the select box options from the database.
- [Django]-How do I extend the Django Group model?
- [Django]-Django form fails validation on a unique field
- [Django]-Using Django time/date widgets in custom form