Reputation: 3752
I've got
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)
I have an instance of Customer, and I'd like to get all Docks that the customer has access to. Customers have access to Suppliers via SupplierCustomerAccess, and Suppliers have access to Docks via SupplierDockAccess. I can do it like so:
# 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]
... but then the resulting list of docks contains duplicates, and I really think it oughtta be possible to do it in one go. Anyone feel like demonstrating some mighty django-fu?
Upvotes: 0
Views: 136
Reputation: 32070
Easiest way I can think of to do this is a combination of ManyToManyField
s and a 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)
...
You should see only one or two hits to the database (depending on if you used select_related
when you got your customer), and your code is insanely clean:
docks = customer.docks()
suppliers = customer.suppliers.all()
...
Upvotes: 2
Reputation: 3752
Alright, I figured it out. One of those things where talking about it seems to do the trick:
docks = Dock.objects.filter(supplierdockaccess__supplier__suppliercustomeraccess__customer=customer).distinct()
...and looking at the sql, it does indeed do it in one big join. Nice. Sorry about answering my own question.
Upvotes: 2