Python 具有多对多字段的django过滤器模型
我的模型:Python 具有多对多字段的django过滤器模型,python,django,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.CharFie
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]的书籍带有
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]
让我们首先用主题检查天真的方法。因此,我们只寻找一本书,因为只有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>]
同:
Book.objects.filter(reduce(lambda q1,q2: q1&q2, [Q(subjects=s) for s in [s1,s2,s3]]))
这是一个更具动态性的版本,您可以轻松地使用主题更改列表
>>> 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\u id
值。
我们得到这样一个查询的原因是,我们在一个.filter
语句中同时应用了所有Q
过滤器。更多关于这方面的信息,请访问
为了从MySQL
的角度获得正确的结果,我们必须为每个主题
加入subjects\u book\u subjects
表以进行筛选。在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())
例如:
>>> reduce(lambda books,s: books.filter(subjects=s),[s1,s2,s3], Book.objects.all())
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` )
INNER JOIN `subjects_book_subjects` T4 ON ( `subjects_book`.`id` = T4.`book_id` )
INNER JOIN `subjects_book_subjects` T6 ON ( `subjects_book`.`id` = T6.`book_id` )
WHERE (
`subjects_book_subjects`.`subject_id` = 1
AND T4.`subject_id` = 2
AND T6.`subject_id` = 3
) LIMIT 21; args=(1, 2, 3)
[<Book: Book object>]
reduce(lambda books,s:books.filter(subjects=s),[s1,s2,s3],Book.objects.all())
调试(0.001)
选择'subjects\u book`.'id`、'subjects\u book`.'title`、'subjects\u book`.'subtitle`
来自“主题书”
上的内部联接'subjects\u book\u subjects'('subjects\u book`.'id`='subjects\u book\u subjects`.'book\u id`)
内部联接'subjects\u book\u subjects'T4 ON('subjects\u book`.'id`=T4.'book\u id`)
内部联接'subjects\u book\u subjects'T6 ON('subjects\u book`.'id`=T6.'book\u id`)
在哪里(
`主题书主题。`subject\u id`=1
和T4.`subject_id`=2
和T6.`subject_id`=3
)限制21;args=(1,2,3)
[]
这是一个猜测,但确实是Book.objects.filter(subjects\uu-in=[2,3,6])
起作用了吗?这显示了所有主题id为2的书籍或主题id为3的书籍或主题id为6的书籍,类似于Book.objects.filter(subjects\uu-id=2)。filter(subjects\uu-id=3)。filter(subjects\uu-id=6)
?你必须在for循环中添加这些。哇。。它正在工作。我不这么认为。谢谢你,汤姆
>>> reduce(lambda books,s: books.filter(subjects=s),[s1,s2,s3], Book.objects.all())
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` )
INNER JOIN `subjects_book_subjects` T4 ON ( `subjects_book`.`id` = T4.`book_id` )
INNER JOIN `subjects_book_subjects` T6 ON ( `subjects_book`.`id` = T6.`book_id` )
WHERE (
`subjects_book_subjects`.`subject_id` = 1
AND T4.`subject_id` = 2
AND T6.`subject_id` = 3
) LIMIT 21; args=(1, 2, 3)
[<Book: Book object>]