多场Django滤波模型

2024-04-25 03:48:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我的型号:

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]的书籍


Tags: true主题modeltitlemodelsnulllengthmax
1条回答
网友
1楼 · 发布于 2024-04-25 03:48:33

带有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>]

我们只是在搜索含有s1s2s3books。这就是为什么我们得到这样的结果。我们有重复的结果,因为我们没有使用.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值。 我们得到这样一个查询的原因是因为我们在一个.filter语句中同时应用了所有的Q过滤器。在Spanning multi-valued relationships上阅读更多关于这个的信息。你知道吗

为了从MySQL的角度获得正确的结果,我们必须为每个要筛选的subject加入subjects_book_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>]

相关问题 更多 >