2010-08-11 2 views
0

У менядовольно сложный Джанго запрос

class Supplier(Model) : 
    pass 

class Customer(Model) : 
    pass 

class Dock(Model) : 
    pass 

class SupplierDockAccess(Model) : 
    supplier = ForeignKey(Supplier) 
    dock = ForeignKey(Dock) 

class SupplierCustomerAccess(Model): 
    supplier = ForeignKey(Supplier) 
    customer = ForeignKey(Customer) 

У меня есть экземпляр Клиента, и я хотел бы получить все доки, что клиент имеет доступ. Клиенты имеют доступ к поставщикам через SupplierCustomerAccess, а поставщики имеют доступ к Docks через SupplierDockAccess. Я могу это сделать так:

# get the suppliers the customer has access to 
supplier_customer_accesses = SupplierCustomerAccess.objects.filter(customer=customer) 
suppliers = [s.supplier for s in supplier_customer_accesses] 

# get the docks those suppliers have access to 
supplier_dock_accesses = SupplierDockAccess.objects.filter(supplier__in=suppliers) 
docks = [s.dock for s in supplier_dock_accesses] 

... но затем полученный список доков содержит дубликаты, и я действительно думаю, что это oughtta можно сделать это на одном дыхании. Кто-нибудь хочет продемонстрировать какой-нибудь могучий джанго-фу?

ответ

2

Проще всего я могу это сделать, это комбинация ManyToManyFields и custom QuerySet/Manager.

from django.db import models 
class CustomQuerySetManager(models.Manager): 
    """ 
     Class for making QuerySet methods available on result set 
     or through the objects manager. 
    """ 
    def get_query_set(self): 
     return self.model.QuerySet(self.model) 
    def __getattr__(self, attr, *args): 
     try:     
      return getattr(self.__class__, attr, *args) 
     except AttributeError: 
      return getattr(self.get_query_set(), attr, *args) 

class Customer(models.Model): 
    suppliers = models.ManyToManyField(through=SupplierCustomerAccess) 
    objects = CustomQuerySetManager() 
    class QuerySet(QuerySet): 
     def docks(self): 
      return Dock.objects.filter(
        supplierdockaccess__supplier__in=self.suppliers 
       ).distinct() 
    ... 
class Supplier(models.Model): 
    docks = models.ManyToManyField(through=SupplierDockAccess) 
    ... 

Вы должны увидеть только один или два удара в базу данных (в зависимости от того, если вы использовали select_related когда вы получили ваш клиент), и ваш код безумно чистый:

docks = customer.docks() 
suppliers = customer.suppliers.all() 
... 
+0

Это также ограничивает вашу JOIN двумя таблицами, а не четырьмя, как ответ OP. –

2

Хорошо, я понял. Одна из тех вещей, где говорят об этом, кажется, сделать трюк:

docks = Dock.objects.filter(supplierdockaccess__supplier__suppliercustomeraccess__customer=customer).distinct() 

... и смотреть на SQL, это действительно сделать это в один большой присоединиться. Ницца. Извините, что ответила на мой вопрос.

+2

Ответ ваш собственный вопрос является прекрасно. Идите и примите ответ, чтобы те, кто придет после, узнают, что вам помогло. –

Смежные вопросы