2010-07-01 1 views
0

У меня есть несколько элементов, которые могут быть одного или нескольких типов.Объединение и запросы Django

class Type(models.Model): 
    name = models.CharField(max_length=128, unique=True) 

class Element(models.Model): 
    name = models.CharField(max_length=128, unique=True) 
    type = models.ManyToManyField('Type') 

Давайте предположим, что у меня есть 3 типа и 3 элемента:

In [3]: Type.objects.all() 
Out[3]: [<Type: His Type>, <Type: My Type>, <Type: Your Type>] 

In [4]: [(e,e.type.all()) for e in Element.objects.all()] 
Out[4]: 
[(<Element: First Element>, [<Type: My Type>]), 
(<Element: Second Element>, [<Type: Your Type>]), 
(<Element: Third Element>, 
    [<Type: My Type>, <Type: Your Type>, <Type: His Type>])] 

Я пытаюсь получить QuerySet с элементами, которые только типа «Мой тип»

Моя идея состояла в том, чтобы получить элементы этого типа и проверить, что они только одного типа.

Но по какой-то причине он считает, что «Третий элемент» имеет только один тип

In [5]: my_type=Type.objects.get(name='My Type') 

In [6]: my_type.element_set.annotate(num_types=Count('type')).filter(num_types__exact=1) 
Out[6]: [<Element: First Element>, <Element: Third Element>] 

In [7]: [(e,e.num_types) for e in my_type.element_set.annotate(num_types=Count('type'))] 
Out[7]: [(<Element: First Element>, 1), (<Element: Third Element>, 1)] 

когда из трех типов

In [8]: Element.objects.get(name='Third Element').type.count() 
Out[8]: 3 

Что я делаю неправильно?

ответ

0

Я закончил делать

Element.objects.filter(pk__in=my_type.element_set.values_list('pk', flat=True)).annotate(num_types=Count('type')).filter(num_types=1)

Таким образом, на меня не влияет соединение, которое происходит, когда я начинаю с my_type.element_set

Если вы задаетесь вопросом о SQL вовлеченного:

В my_type.element_set.annotate(num_types=Count('type')).filter(num_types__exact=1) он делает:

SELECT `senalesweb_element`.`id`, `senalesweb_element`.`name`, COUNT(`senalesweb_element_type`.`type_id`) AS `num_types` FROM `senalesweb_element` LEFT OUTER JOIN `senalesweb_element_type` ON (`senalesweb_element`.`id` = `senalesweb_element_type`.`element_id`) WHERE (`senalesweb_element_type`.`type_id` = 1) GROUP BY `senalesweb_element`.`id` HAVING COUNT(`senalesweb_element_type`.`type_id`) = 1 ORDER BY NULL LIMIT 21 

В то время как в Element.objects.filter(pk__in=my_type.element_set.values_list('pk', flat=True)).annotate(num_types=Count('type')).filter(num_types=1) он делает:

SELECT `senalesweb_element`.`id`, `senalesweb_element`.`name`, COUNT(`senalesweb_element_type`.`type_id`) AS `num_types` FROM `senalesweb_element` LEFT OUTER JOIN `senalesweb_element_type` ON (`senalesweb_element`.`id` = `senalesweb_element_type`.`element_id`) WHERE (`senalesweb_element`.`id` IN (SELECT U0.`id` FROM `senalesweb_element` U0 INNER JOIN `senalesweb_element_type` U1 ON (U0.`id` = U1.`element_id`) WHERE U1.`type_id` = 1)) GROUP BY `senalesweb_element`.`id` HAVING COUNT(`senalesweb_element_type`.`type_id`) = 1 ORDER BY NULL LIMIT 21 
0

Учитывая, что это стандартное много-ко-многим, вы можете вызвать 'my_type.element_set.all()', который должен предоставить вам экземпляры Element этого типа. Вы также можете пойти другим способом (используя множественную версию имени модели, а не «_set»), чтобы получить такие типы элементов: «my_element.types.all()».

Чтобы получить счет, просто сделайте «my_type.element_set.count()».

0

SQL-запрос, выполняемый Django за кулисами, ограничивает вас только строками, содержащими type_element_type.element_id = 1. Таким образом, аннотированный счет применяется только к этим, следовательно, по 1 каждому.

Ваш запрос [7] выше становится этот SQL (как показано this):

SELECT "type_element"."id", "type_element"."name", COUNT("type_element_type"."type_id") 
AS "num_types" FROM "type_element" LEFT OUTER JOIN "type_element_type" 
ON ("type_element"."id" = "type_element_type"."element_id") 
WHERE "type_element_type"."type_id" = 1 
GROUP BY "type_element"."id", "type_element"."name" 

и от sqlite3:

sqlite> SELECT * FROM "type_element" LEFT OUTER JOIN "type_element_type" ON ("type_element"."id" = "type_element_type"."element_id") WHERE "type_element_type"."type_id" = 1; 
1|First Element|1|1|1 
3|Third Element|3|3|1 
+0

Я предположил, что причиной была присоединиться , но я спрашивал о возможности переписать запрос. В итоге я нашел решение. – naw

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

  • Нет связанных вопросов^_^