2015-10-13 18 views
5

Może brakuje mi czegoś oczywistego, ale mam problem z uzyskaniem połączenia w polu ManyToMany, aby pracować w aplikacji Django. Mam dwa modele:Łączenie pól ManyToMany z prefetch_related w Django

class Area(models.Model): 
    name = CharField(...) 

class Role(models.Model): 
    name = CharField(...) 
    areas = ManyToManyField('Area', ...) 

Moim celem jest mieć równowartość tej kwerendy:

select a.name, r.name 
from area a 
join area_role ar on ar.area_id = a.id 
join role r on ar.role_id = r.id 
order by a.name, r.name 

Otrzymany zbiór danych będzie wyglądać następująco:

Area Role 
--------------------- 
A  My Role 
A  Your Role 
A  Yo Mamas Role 
B  My Role 
B  Some Other Role 

Jak widać w na przykład element Moja Rola pojawia się dwukrotnie, raz dla każdego obszaru. Wiem, że mogę uzyskać listę obszarów, a następnie uzyskać listę ról dla każdej z nich (w wyniku zapytania N + 1), ale chciałbym, jeśli to możliwe, być skuteczny. W związku z tym stwierdziłem, że może być to, co chciałem użyć. Kiedy używam tego, jednak kończę z wszystkimi wartościami obszaru jako Brak. Oto, co próbowałem:

rqs = (Role.objects.filter(areas__id__in=[1,2,3]) 
     .prefetch_related(areas).order_by('areas__name', 'name')) 

for r in rqs: 
    print("Area Name: " + str(r.areas.name)) 
    print("Role Name: " + str(r.name)) 

Nazwy ról pojawiają się poprawnie, ale nazwy obszarów nie. Co ja tu robię źle?

+0

niepowiązanych: dlaczego umieścić 'Area' między cudzysłowami w' areas = ManyToManyField ('Area', ...) '? – Pynchia

+1

'print (" Nazwa obszaru: "+ str (r.nazwa _nazwa))' miałeś na myśli 'print (" Area Name: "+ str (r.areas.name))'? – danielcorreia

+1

@Pynchia mógł używać obszaru bez cudzysłowów w tym przypadku, ponieważ obszar jest zdefiniowany przed modelem ról. Jeśli obszar został zdefiniowany po roli, wymagane byłyby wyceny. [Pełne wyjaśnienie tutaj] (https://docs.djangoproject.com/en/1.8/ref/models/fields/#lazy-relationships) – danielcorreia

Odpowiedz

5

Nie można uzyskać dostępu do r.areas__name dla roli r. Nadal musisz uzyskać dostęp do ról za pośrednictwem r.areas.all(). Jednak za pomocą prefetch_related można pobrać wszystkie powiązane obiekty w jednym zapytaniu dodatkowym zamiast w zapytaniach O (n).

Ponieważ chcesz zamówić według nazwy obszaru, prawdopodobnie powinieneś użyć modelu Area dla swojego zestawu zapytań, a następnie przepętlaj powiązane role.

areas = Area.objects.filter(id__in=[1, 2, 3]).order_by('name').prefetch_related('role_set') 

for area in areas: 
    roles = area.role_set.all() 
    for role in roles: 
     print area.name, roles.name 

To powinno dać ci kolejność chcesz, tak długo, jak model Role jest nakazany przez name domyślnie. Jeśli nie, możesz użyć obiektu Prefetch, aby zamówić powiązany zestaw zapytań.

Powiązane problemy