2011-07-31 9 views
16

Một trong những mô hình của tôi đặc biệt phức tạp. Khi tôi cố gắng chỉnh sửa nó trong Django Admin nó thực hiện 1042 truy vấn và mất hơn 9 giây để xử lý.Làm thế nào để buộc Django Admin sử dụng select_related?

Tôi biết tôi có thể thay thế một số menu thả xuống với raw_id_fields, nhưng tôi nghĩ rằng nút cổ chai lớn hơn là nó không thực hiện select_related() như mong muốn.

Tôi có thể yêu cầu trang web quản trị viên thực hiện việc này không?

Trả lời

8

Đối với mô hình cụ thể của tôi, khía cạnh đặc biệt chậm đi qua ForeignKeys khi chúng được hiển thị dưới dạng, không được gọi là sử dụng select_related, vì vậy đó là phần tôi sẽ tăng tốc.

Nhìn qua các nguồn django có liên quan, bạn sẽ thấy trong django/contrib/admin/options.py rằng phương pháp formfield_for_foreignkeys mất mỗi FK db_field và gọi ForeignKey phương pháp formfield lớp, được định nghĩa trong django/db/mô hình/lĩnh vực/liên quan/như:

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) 

Từ điều này, chúng tôi thấy nếu chúng tôi cung cấp db_field với kwargs['queryset'], chúng tôi có thể xác định bộ truy vấn tùy chỉnh sẽ sử dụng select_related (điều này có thể được cung cấp bởi formfield_for_foreignkey).

Vì vậy, về cơ bản những gì chúng tôi muốn làm là ghi đè admin.ModelAdmin với SelectRelatedModelAdmin và sau đó làm cho lớp con ModelAdmin của chúng ta về SelectRelatedModelAdmin thay vì 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) 

mẫu mã này không bao gồm quản trị Inline s hoặc ManyToManyField s, hoặc foreign_key traversal trong các hàm được gọi là readonly_fields hoặc truy vấn select_related tùy chỉnh, nhưng cách tiếp cận tương tự sẽ hoạt động đối với những trường hợp đó.

+0

Điều này có thể được đơn giản hóa bởi 'get_field_queryset' (mặc dù nó là không có giấy tờ và do đó có thể thay đổi trong tương lai). –

25

Mặc dù câu trả lời của câu trả lời của jimbob có ý nghĩa, vì nhu cầu của tôi, tôi chỉ có thể ghi đè phương thức get_queryset() với một lớp lót, thậm chí chọn khóa ngoài của khóa ngoài. Có lẽ điều này có thể hữu ích cho ai đó.

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 đã được đổi tên thành [get_queryset] (https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_queryset) – phoibos

+0

Điều này dường như không hoạt động trang ** thay đổi **. Tức là, biểu mẫu vẫn đang tạo hàng nghìn truy vấn để có được các cá thể liên quan. Là nó chỉ cho tôi, hoặc không làm việc này cho người khác? django == 1.11.3 –

16

bạn có thể thử này

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

Thật không may tôi không nghĩ rằng điều này giải quyết các truy vấn ban đầu. Thuộc tính 'list_select_related' là dành cho trang Admin _listing_ và không phải là trang _edit_ của đối tượng. Chắc chắn trong kinh nghiệm của tôi với cùng một vấn đề này, có bộ danh sách liên quan không tăng tốc độ chỉnh sửa trang, chỉ danh sách và chọn trang. –