Django:过滤多对多字段时重复

标签 django django-models django-queryset

我的 Django 应用程序中有以下模型:

class Book(models.Model):
    name = models.CharField(max_length=100)
    keywords = models.ManyToManyField('Keyword')

class Keyword(models.Model)
    name = models.CharField(max_length=100)

我保存了以下关键字:
science-fiction
fiction
history
science
astronomy

在我的网站上,用户可以通过访问 /keyword-slug/ 来按关键字过滤书籍。在我的 View 中,keyword_slug 变量被传递给一个函数,它按关键字过滤 Books,如下所示:
def get_books_by_keyword(keyword_slug):
    books = Book.objects.all()
    keywords = keyword_slug.split('-')
    for k in keywords:
        books = books.filter(keywords__name__icontains=k)

这在大多数情况下有效,但是每当我使用包含在关键字表中出现多次的字符串(例如 science-fictionfiction )的关键字进行过滤时,我就会得到同一本书在结果查询集中出现多次。

我知道我可以添加 distinct 来只返回独特的书籍,但我想知道为什么我会从一开始就得到重复,并且真的想了解为什么会这样。由于我只在成功过滤的 QuerySet 上调用 filter(),如何将重复的书添加到结果中?

最佳答案

您示例中的 2 个模型用 3 个表表示: bookkeywordbook_keyword 关系表来管理 M2M 字段。

当您在过滤器调用中使用 keywords__name 时,Django 正在使用 SQL JOIN 来合并所有 3 个表。这允许您通过另一个表中的值过滤第一个表中的对象。

SQL 将是这样的:

SELECT `book`.`id`,
       `book`.`name`
FROM `book`
INNER JOIN `book_keyword` ON (`book`.`id` = `book_keyword`.`book_id`)
INNER JOIN `keyword` ON (`book_keyword`.`keyword_id` = `keyword`.`id`)
WHERE (`keyword`.`name` LIKE %fiction%)

加入后你的数据看起来像
| Book Table          | Relation table                     | Keyword table                |
|---------------------|------------------------------------|------------------------------|
| Book ID | Book name | relation_book_id | relation_key_id | Keyword ID | Keyword name    |
|---------|-----------|------------------|-----------------|------------|-----------------|
| 1       | Book 1    | 1                | 1               | 1          | Science-fiction |
| 1       | Book 1    | 1                | 2               | 2          | Fiction         |
| 2       | Book 2    | 2                | 2               | 2          | Fiction         |

然后,当数据从 DB 加载到 Python 时,您只能从 book 表中接收数据。如您所见,第 1 本书在那里重复

这就是多对多关系和 JOIN 的工作原理

关于Django:过滤多对多字段时重复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18071572/

相关文章:

python - 为什么在 Google App Engine 上使用 XHTML2PDF 时我的图像不显示?

跨越关系但以编程方式进行的 Django 过滤器?

python - 从管理界面保存时未触发 Django 信号

python - Django QuerySet values_list 返回未知字符

python - QuerySet 对象在 Django Rest Framework 上没有属性 'user'

django - 带有客户端更新的异步 View ,用于长时间运行的进程/ View

django - 在 ModelAdmin.readonly_fields 方法中使用模板标签

python - 如何设置路径以将数据从 CSV 文件加载到 Docker 容器中的 PostgreSQL 数据库中?

Django - 上传静态图像作为默认 ImageField 文件

sql - Django 查询用于列出相关查询中的所有外键对象