python - Django - 过滤同一张表上的多个外键
问题描述
假设我有这样的模型:
class Story(...):
name = models.CharField(...)
class Chapter(...):
title = models.CharField(...)
length = models.CharField(...)
story = models.ForeignKey('Story', ..., related_name='chapters')
如何过滤具有 N 个特定章节的故事,即:
titles = ['Beginning', 'Middle', 'End']
length = 'Long'
# is there a better way to do this?
stories_with_these_chapters = Story.objects.filter(
chapters__title = titles[0],
chapters__length = length
).filter(
chapters__title = titles[1],
chapters__length = length
).filter(
chapters__title = titles[2],
chapters__length = length
)
编辑:
所以例如说我有这个数据:
故事:
ID | Name
-- | ----
1 | First Story
2 | Second Story
3 | Third Story
章节:
ID | Story ID | Title | Length
-- | -------- | --------- | ------
1 | 1 | Beginning | Long
2 | 1 | End | Long
3 | 2 | Beginning | Short
4 | 2 | Middle | Short
5 | 2 | End | Short
6 | 3 | Beginning | Long
7 | 3 | Middle | Long
8 | 3 | End | Long
我想过滤标题为“开始”、“中间”和“结束”并且是“长”的章节的故事——在这个例子中这只是故事 3,因为故事 1 没有标题为“中间”的章节和故事 2 中的所有章节都是“短”的。
解决方案
好的,尝试解释过滤和等效的方法。
如果您正在使用模型字段或ForeignKey
关系:
.filter(A, B)
方法WHERE A AND B
.filter(Q(A) & Q(B))
方法WHERE A AND B
.filter(A).filter(B)
方法WHERE A AND B
具有反向ForeignKey
关系:
.filter(A, B)
方法WHERE A AND B
.filter(Q(A) & Q(B))
方法WHERE A AND B
.filter(A).filter(B)
表示WHERE A AND B
但A 和 B 由于多个 INNER JOIN 对具有重复项的表进行操作
看到您的编辑后,您需要链接 Q 查询。
from functools import reduce
import operator
from django.db.models import Q
query = reduce(operator.and_, (Q(chapter__title__contains=x) for x in titles))
qs = Story.objects.filter(query)
将其放入终端以显示 reduce 正在为您做什么;
>>> from functools import reduce
>>>
>>> import operator
>>> from django.db.models import Q
>>>
>>> titles = ['Beginning', 'Middle', 'End']
>>> query = reduce(operator.and_, (Q(chapter__title__contains=x) for x in titles))
>>> query
<Q: (AND: ('chapter__title__contains', 'Beginning'), ('chapter__title__contains', 'Middle'), ('chapter__title__contains', 'End'))>
reduce
应用一些数学来构建一个Q
对象,该对象AND
为列表匹配中的每个术语执行一个对象也是如此chapter__title__contains
添加长度列的更新
从上面,您可以添加到query
添加长度查询;
>>> query.add(Q(chapter__length='Long'), query.connector)
<Q: (AND: ('chapter__title__contains', 'Beginning'), ('chapter__title__contains', 'Middle'), ('chapter__title__contains', 'End'), ('chapter__length', 'Long'))>
附带说明一下,要查看 django 查询背后的 SQL 是什么,您可以执行以下操作;
>>> query = User.objects.all()
>>> query.query
<django.db.models.sql.query.Query object at 0x7f910f250eb0>
>>> print(query.query)
SELECT "authentication_user"."id", "authentication_user"."password", "authentication_user"."last_login", "authentication_user"."is_superuser", "authentication_user"."email", "authentication_user"."first_name", "authentication_user"."last_name", "authentication_user"."date_joined", "authentication_user"."is_active", "authentication_user"."is_staff" FROM "authentication_user"
推荐阅读
- kubernetes - 尝试读取入口列表正文时,Kubernetes 客户端给出了 Forebidden 错误
- android - Android:前导减号后无法在EditText字段中输入任何数字
- node.js - JWT 令牌验证以解码和保护 MEAN 应用程序中的路由
- javascript - TypeError:无法读取未定义的属性(读取“长度”) - Shopify,Vuejs
- c++ - 从向量中删除元素后程序崩溃
- javascript - JavaScript:如何在指定时区获取日期时间
- flutter - 'Pub get' 并且无法构建颤振项目
- javascript - HTML to pdf:容器内容溢出时跳转到新页面
- sql - Oracle SQL - 仅按附近相同的记录分组
- vba - Excel VBA将文本文件导入excel并将特定列转换为文本