1 | from django.db import models, connection
|
---|
2 | from django.contrib.auth.models import Group
|
---|
3 |
|
---|
4 | class A(models.Model):
|
---|
5 | name = models.CharField(max_length=150)
|
---|
6 |
|
---|
7 | def __unicode__(self):
|
---|
8 | return self.name
|
---|
9 |
|
---|
10 | class AGroups(models.Model):
|
---|
11 | a = models.ForeignKey(A, related_name='groups')
|
---|
12 | group = models.ForeignKey(Group)
|
---|
13 |
|
---|
14 | class B(models.Model):
|
---|
15 | name = models.CharField(max_length=150)
|
---|
16 | a = models.ForeignKey(A)
|
---|
17 |
|
---|
18 | def __unicode__(self):
|
---|
19 | return self.name
|
---|
20 |
|
---|
21 | class C(models.Model):
|
---|
22 | name = models.CharField(max_length=150)
|
---|
23 | b = models.ForeignKey(B)
|
---|
24 |
|
---|
25 | def __unicode__(self):
|
---|
26 | return self.name
|
---|
27 |
|
---|
28 |
|
---|
29 |
|
---|
30 | def testdata():
|
---|
31 | # Order of creation is important; we need mixed-up IDs
|
---|
32 | a1 = A.objects.create(name='1')
|
---|
33 | a2 = A.objects.create(name='2')
|
---|
34 | a3 = A.objects.create(name='3')
|
---|
35 | b3 = B.objects.create(name='3', a=a3)
|
---|
36 | b1 = B.objects.create(name='1', a=a1)
|
---|
37 | b2 = B.objects.create(name='2', a=a2)
|
---|
38 | c2 = C.objects.create(name='2', b=b2)
|
---|
39 | c3 = C.objects.create(name='3', b=b3)
|
---|
40 | c1 = C.objects.create(name='1', b=b1)
|
---|
41 |
|
---|
42 | g1 = Group.objects.create(name='1')
|
---|
43 | AGroups.objects.create(a=a1, group=g1)
|
---|
44 | g2 = Group.objects.create(name='2')
|
---|
45 | AGroups.objects.create(a=a2, group=g2)
|
---|
46 |
|
---|
47 |
|
---|
48 | def testquery():
|
---|
49 | g1 = Group.objects.get(name='1')
|
---|
50 | g2 = Group.objects.get(name='2')
|
---|
51 | print 'all', C.objects.all()
|
---|
52 |
|
---|
53 | for gs in [[g1], [g2], [g1, g2], []]:
|
---|
54 | print '==='
|
---|
55 | print 'groups', gs
|
---|
56 | print 'filter', C.objects.filter(b__a__groups__group__in=gs)
|
---|
57 | print 'exclude', C.objects.exclude(b__a__groups__group__in=gs)
|
---|
58 | # What I expect:
|
---|
59 | print (set(C.objects.all()) ==
|
---|
60 | set(C.objects.filter(b__a__groups__group__in=gs)) |
|
---|
61 | set(C.objects.exclude(b__a__groups__group__in=gs)))
|
---|
62 |
|
---|
63 | """
|
---|
64 | What I get:
|
---|
65 |
|
---|
66 | all [<C: 2>, <C: 3>, <C: 1>]
|
---|
67 | ===
|
---|
68 | groups [<Group: 1>]
|
---|
69 | filter [<C: 1>]
|
---|
70 | exclude [<C: 3>, <C: 1>]
|
---|
71 | False
|
---|
72 | ===
|
---|
73 | groups [<Group: 2>]
|
---|
74 | filter [<C: 2>]
|
---|
75 | exclude [<C: 2>, <C: 1>]
|
---|
76 | False
|
---|
77 | ===
|
---|
78 | groups [<Group: 1>, <Group: 2>]
|
---|
79 | filter [<C: 1>, <C: 2>]
|
---|
80 | exclude [<C: 1>]
|
---|
81 | False
|
---|
82 | ===
|
---|
83 | groups []
|
---|
84 | filter []
|
---|
85 | exclude [<C: 2>, <C: 3>, <C: 1>]
|
---|
86 | True
|
---|
87 | """
|
---|
88 |
|
---|
89 | def debugquery():
|
---|
90 | g1 = Group.objects.get(name='1')
|
---|
91 | print C.objects.exclude(b__a__groups__group__in=[g1])
|
---|
92 | print connection.queries[-1]['sql']
|
---|
93 |
|
---|
94 | """
|
---|
95 | SELECT "books_c"."id", "books_c"."name", "books_c"."b_id" FROM "books_c"
|
---|
96 | INNER JOIN "books_b" ON ("books_c"."b_id" = "books_b"."id")
|
---|
97 | INNER JOIN "books_a" ON ("books_b"."a_id" = "books_a"."id")
|
---|
98 | WHERE NOT (("books_b"."a_id" IN -- should be: "books_b"."id" IN
|
---|
99 | (SELECT U1."id" FROM "books_b" U1
|
---|
100 | INNER JOIN "books_a" U2 ON (U1."a_id" = U2."id")
|
---|
101 | INNER JOIN "books_agroups" U3 ON (U2."id" = U3."a_id")
|
---|
102 | WHERE (U3."group_id" IN (1) AND U1."id" IS NOT NULL))
|
---|
103 | AND "books_a"."id" IS NOT NULL)) LIMIT 21
|
---|
104 | """
|
---|
105 |
|
---|
106 | # Slightly different query which should get the same result leads
|
---|
107 | # to the same bug:
|
---|
108 | agroups = AGroups.objects.filter(group__in=[g1])
|
---|
109 | print agroups
|
---|
110 | print C.objects.exclude(b__a__groups__in=agroups)
|
---|
111 | print connection.queries[-1]['sql']
|
---|
112 |
|
---|
113 | """
|
---|
114 | SELECT "books_c"."id", "books_c"."name", "books_c"."b_id" FROM "books_c"
|
---|
115 | INNER JOIN "books_b" ON ("books_c"."b_id" = "books_b"."id")
|
---|
116 | INNER JOIN "books_a" ON ("books_b"."a_id" = "books_a"."id")
|
---|
117 | WHERE NOT (("books_b"."a_id" IN -- should be: "books_b"."id" IN
|
---|
118 | (SELECT U1."id" FROM "books_b" U1
|
---|
119 | INNER JOIN "books_a" U2 ON (U1."a_id" = U2."id")
|
---|
120 | INNER JOIN "books_agroups" U3 ON (U2."id" = U3."a_id")
|
---|
121 | WHERE (U3."id" IN (SELECT U0."id" FROM "books_agroups" U0
|
---|
122 | WHERE U0."group_id" IN (1)) AND U1."id" IS NOT NULL))
|
---|
123 | AND "books_a"."id" IS NOT NULL)) LIMIT 21
|
---|
124 | """
|
---|
125 |
|
---|
126 | # This works:
|
---|
127 | b_excludes = B.objects.filter(a__groups__group__in=[g1])
|
---|
128 | print C.objects.exclude(b__in=b_excludes)
|
---|
129 | print connection.queries[-1]['sql']
|
---|
130 |
|
---|