2009-09-14 22 views
9

mam modele, bardziej lub mniej więcej tak:Django LEFT DOŁĄCZ?

class ModelA(models.Model): 
    field = models.CharField(..) 

class ModelB(models.Model): 
    name = models.CharField(.., unique=True) 
    modela = models.ForeignKey(ModelA, blank=True, related_name='modelbs') 

    class Meta: 
     unique_together = ('name','modela') 

chcę zrobić kwerendę, która mówi coś takiego: „Get cały MODELA jest gdzie nazwa pola jest równa X, które mają wzór ModelB z nazwą X lub bez nazwy modelu w ogóle”

do tej pory mam to:

to dostanie mi wszystkie ModelAs, które mają co najmniej jeden modelB (aw rzeczywistości to zawsze będzie tylko jeden) - ale jeśli Model A nie ma powiązanego ModelB, to nie t być w zestawie wyników. Potrzebuję go w zestawie wyników z czymś takim jak obj.modelb = Brak

Jak mogę to zrobić?

+3

Na marginesie: to tak naprawdę pomaga, jeśli stosowane opisowych nazw, takich jak typowy scenariusz Blog/lub post co najmniej Foo/Bar zamiast ModelA/ModelB, które są nieintuicyjne i po prostu trudne do odczytania/rozróżnienia. –

Odpowiedz

11

Użyj Q, aby połączyć dwa warunki:

from django.db.models import Q 
qs = ModelA.objects.exclude(field=condition) 
qs = qs.filter(Q(modelbs__name=condition) | Q(modelbs__isnull=True)) 

Aby zbadać wynikowy kwerendy SQL:

print qs.query.as_sql() 

Na podobnym zapytaniu, to generuje LEFT OUTER JOIN ... WHERE (a .val = b LUB a.id IS NULL).

+0

Nie ma dla mnie znaczenia –

+3

Jeśli wszystko, co chcesz powiedzieć, "nie działa", z pewnością nie mogę ci pomóc. Czy zbadałeś nawet kod SQL? –

+0

Tak, zrobiłem. Przepraszam, że nie byłam bardziej konkretna. Byłem zajęty próbowaniem sugestii, którą znalazłem gdzie indziej. Tak czy inaczej, robienie tego, co masz, powoduje, że warunek łączenia jest w klauzuli where, w przeciwieństwie do klauzuli ON, nie jestem do końca pewien, dlaczego, ale powoduje to, że lewe sprzężenie nie działa zgodnie z oczekiwaniami. Jeśli ręcznie uruchomię to samo zapytanie, gdy warunek zostanie przeniesiony do pozycji ON, działa tak, jak chcę. –

-3

LEFT JOIN jest połączeniem dwóch zapytań. Czasami jest zoptymalizowany do jednego zapytania. Czasami nie jest właściwie zoptymalizowany przez podstawowy silnik SQL i jest wykonywany jako dwa osobne zapytania.

Zrób to.

for a in ModelA.objects.all(): 
    related = a.model_b.set().all() 
    if related.count() == 0: 
     # These are the A with no B's 
    else: 
     # These are the A with some B's 

Nie fetyszyzuj o łączeniach zewnętrznych SQL, które wyglądają jak "pojedyncze" zapytanie.

+5

To ma lat, ale chcę zauważyć, straszne porady. Kończy to uruchamianie ogromnej liczby zapytań za kulisami i może z łatwością doprowadzić do zindeksowania witryny. –

1

Wygląda na to, że zbliżasz się do bariery 80%. Dlaczego nie po prostu użyć .extra(select={'has_x_or_none':'(EXISTS (SELECT ...))'}), aby wykonać podzapytanie? Możesz napisać podzapytanie w dowolny sposób i powinieneś móc filtrować w odniesieniu do nowego pola. SQL powinien skończyć patrząc coś takiego:

SELECT *, 
    ((EXISTS (SELECT * FROM other WHERE other.id=primary.id AND other.name='X')) 
    OR (NOT EXISTS (SELECT * FROM other WHERE other.id=primary.id))) AS has_x_or_none 
    FROM primary WHERE has_x_or_none=1;