25

Tôi có hai Mô hình ở Django. Đầu tiên có thứ bậc của các chức năng công việc (vị trí) báo cáo đến vị trí nào khác, và thứ hai là con người và chức năng công việc mà họ nắm giữ.select_related with reverse foreign keys

class PositionHierarchy(model.Model): 
    pcn = models.CharField(max_length=50) 
    title = models.CharField(max_length=100) 
    level = models.CharField(max_length=25) 
    report_to = models.ForeignKey('PositionHierachy', null=True) 


class Person(model.Model): 
    first_name = models.CharField(max_length=50) 
    last_name = models.CharField(max_length=50) 
    ... 
    position = models.ForeignKey(PositionHierarchy) 

Khi tôi có một kỷ lục Person và tôi muốn tìm quản lý của người đó, tôi phải làm

manager = person.position.report_to.person_set.all()[0] 
# Can't use .first() because we haven't upgraded to 1.6 yet 

Nếu tôi nhận được những người có QuerySet, tôi có thể tham gia (và tránh thứ hai chuyến đi đến cơ sở dữ liệu) với vị trí và report_to sử dụng Person.objects.select_related('position', 'position__reports_to').filter(...), nhưng có cách nào để tránh thực hiện một chuyến đi đến cơ sở dữ liệu để có được person_set? Tôi đã thử thêm 'position__reports_to__person_set' hoặc chỉ position__reports_to__person vào select_related, nhưng điều đó dường như không thay đổi truy vấn. Đây có phải là những gì prefetch_related là dành cho?

Tôi muốn thực hiện một trình quản lý tùy chỉnh để khi tôi thực hiện truy vấn để nhận được bản ghi Person, tôi cũng có được PositionHeirarchy và bản ghi Người quản lý của họ mà không cần phải thực hiện thêm các chuyến đi vòng vào cơ sở dữ liệu. Đây là những gì tôi có cho đến thời điểm này:

class PersonWithManagerManager(models.Manager): 
    def get_query_set(self): 
     qs = super(PersonWithManagerManager, self).get_query_set() 
     return qs.select_related(
      'position', 
      'position__reports_to', 
     ).prefetch_related(
     ) 
+0

lẽ là một lỗi đánh máy, nhưng nó phải được 'get_queryset()', không phải 'get_query_set'. – Paolo

+1

@Paolo nó là 'get_query_set' trong Django 1.5. –

Trả lời

25

Có, đó là những gì prefetch_related() là dành cho. Nó sẽ yêu cầu một truy vấn bổ sung, nhưng ý tưởng là nó sẽ nhận được tất cả các thông tin liên quan cùng một lúc, thay vì một lần cho mỗi Person.

Trong trường hợp của bạn:

qs.select_related('position__report_to') 
    .prefetch_related('position__report_to__person_set') 

nên yêu cầu hai truy vấn, không phụ thuộc vào số lượng Persons trong tập truy vấn ban đầu.

Hãy so sánh ví dụ này từ documentation:

>>> Restaurant.objects.select_related('best_pizza') 
         .prefetch_related('best_pizza__toppings')