具有多对多字段的django过滤器模型

时间:2015-02-06 06:23:28

标签: python django

我的模特:

class Book(models.Model):
    title = models.CharField(max_length=254)
    subtitle = models.CharField(max_length=254, null=True, blank=True)
    subjects = models.ManyToManyField(Subject)

class Subject(models.Model):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=254, null=True, blank=True)

这里的书籍主题与主题模型存在多对多的关系。

我如何才能获得所有相同主题的书籍。 例如,所有具有主题id [2,3,6]

的书籍

1 个答案:

答案 0 :(得分:0)

Q个对象的解决方案无效。

一些初步数据:

>>> from subjects.models import Subject, Book
>>> s1 = Subject.objects.create(name='subject_1', description='description 1')
>>> s2 = Subject.objects.create(name='subject_2', description='description 2')
>>> s3 = Subject.objects.create(name='subject_3', description='description 3')
>>> s4 = Subject.objects.create(name='subject_4', description='description 4')
>>> b1 = Book.objects.create(title='one_subject_book', subtitle='one subject')
>>> b1.subjects = [s1]
>>> b2 = Book.objects.create(title='three_subject_book', subtitle='three subjects')
>>> b2.subjects = [s2,s3,s4]
>>> b3 = Book.objects.create(title='four_subject_book', subtitle='four subjects')
>>> b3.subjects = [s1,s2,s3,s4]

让我们先用subjects__in=[s1,s2,s3]检查天真的方法。我们只搜索一本书,因为只有b3包含所有科目。

>>> Book.objects.filter(subjects__in=[s1,s2,s3])
DEBUG (0.001) 
    SELECT `subjects_book`.`id`, `subjects_book`.`title`, `subjects_book`.`subtitle` 
    FROM `subjects_book` 
    INNER JOIN `subjects_book_subjects` ON ( `subjects_book`.`id` = `subjects_book_subjects`.`book_id` ) 
    WHERE `subjects_book_subjects`.`subject_id` IN (1, 2, 3) 
    LIMIT 21; args=(1, 2, 3)
[<Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>]

我们只是搜索包含bookss1s2的{​​{1}}。这就是为什么我们得到这样的结果。我们有重复的结果,因为我们没有使用s3

现在让我们试试.distinct()个对象。

只想提一下:

Q

与:

相同
Book.objects.filter(Q(subjects=s1)&Q(subjects=s2)&Q(subjects=s3))

只是一个更动态的版本,您可以使用Book.objects.filter(reduce(lambda q1,q2: q1&q2, [Q(subjects=s) for s in [s1,s2,s3]])) 轻松更改列表。

subjects

我们得到了空结果集。如果您检查查询,这将是正常的。我们不能同时包含三个不同的>>> Book.objects.filter(reduce(lambda q1,q2: q1&q2, [Q(subjects=s) for s in [s1,s2,s3]])) DEBUG (0.260) SELECT `subjects_book`.`id`, `subjects_book`.`title`, `subjects_book`.`subtitle` FROM `subjects_book` INNER JOIN `subjects_book_subjects` ON ( `subjects_book`.`id` = `subjects_book_subjects`.`book_id` ) WHERE ( `subjects_book_subjects`.`subject_id` = 1 AND `subjects_book_subjects`.`subject_id` = 2 AND `subjects_book_subjects`.`subject_id` = 3 ) LIMIT 21; args=(1, 2, 3) [] 值。 我们得到此类查询的原因是因为我们在单subject_id语句中同时应用了所有Q过滤器。请在Spanning multi-valued relationships了解详情。

为了从.filter的角度获得正确的结果,我们必须为我们想要过滤的每个MySQL加入subjects_book_subjects表。在subject视角中,这可以通过一系列连续的ORM语句来实现:

.filter

以更时尚的方式看待这个:

Book.objects.filter(subjects=s1).filter(subjects=s2).filter(subjects=s3)

例如:

books_queryset = reduce(lambda books,s: books.filter(subjects=s),[s1,s2,s3], Book.objects.all())