使用 Q-Objects 在 Django 中动态构建复杂查询
Posted
技术标签:
【中文标题】使用 Q-Objects 在 Django 中动态构建复杂查询【英文标题】:Dynamically building complex queries in Django using Q-Objects 【发布时间】:2021-01-17 01:30:59 【问题描述】:我有一些项目的数据库查询 (containers
)。
有一个相关表可以定义一些限制(仅限***org
中的那些,只有dept
中的那些(客户) org
,或者只是 team
中的那些(课程)在 dept
)
这是获取对象列表的(不工作的)代码:
def get_containers(customer_id: None, course_id: None):
q_list = (Q(is_private=False), Q(is_active=True))
if customer_id:
try:
customers = Customer.objects
customer = customers.get(id=customer_id)
except KeyError:
return None
# Second level restriction: may have a customer-id
customer_q = (
Q(restrictions__customer__isnull=True)
| Q(restrictions__customer=customer.id)
)
# Third level restriction: may be restricted to a course-code
course_q = Q(restrictions__course_code__isnull=True)
if course_id:
course_q |= Q(restrictions__course_code=course_id)
# Top level restriction: restricted to org
restrictions_q = (
Q(restrictions__organisation=customer.org.id)
& customer_q
& course_q
)
q_list = (Q(q_list) | Q(restrictions_q))
print(f"q_list: q_list")
return Container.objects.filter(q_list)
在此期间,我一直使用https://docs.djangoproject.com/en/3.0/topics/db/queries/#complex-lookups-with-q(和引用的https://github.com/django/django/blob/master/tests/or_lookups/tests.py)和之前询问的django dynamically filtering with q objects 作为引用。
我尝试了很多变体来让if customer_id:
块末尾的OR
工作 - 它们都给了我错误:
q_list = q_list | restrictions_q\nTypeError: unsupported operand type(s) for |: 'tuple' and 'Q'
q_list = Q(q_list | restrictions_q)\nTypeError: unsupported operand type(s) for |: 'tuple' and 'Q'
q_list |= restrictions_q\nTypeError: unsupported operand type(s) for |=: 'tuple' and 'Q'
q_list.add(restrictions_q, Q.OR)\nAttributeError: 'tuple' object has no attribute 'add'
问题:如何创建q_list = q_list OR restrictions_q
构造?
【问题讨论】:
【参考方案1】:嗯,主要问题似乎是您有元组和列表而不是 Q
对象。例如,行
q_list = (Q(is_private=False), Q(is_active=True))
会像这些选项中的任何一个一样更正确:
q_obj = Q(Q(is_private=False), Q(is_active=True))
q_obj = Q(is_private=False, is_active=True)
但也许你的整个方法可以稍微修改一下。
def get_containers(customer_id=None, course_id=None):
customer_q = Q()
org_q = Q()
if customer_id:
# this may raise Customer.DoesNotExist error
customer = Customer.objects.get(id=customer_id)
# Top level restriction: restricted to org
org_q = Q(restrictions__organisation=customer.org.id)
# Second level restriction: may have a customer-id
customer_q = Q(
Q(restrictions__customer__isnull=True)
| Q(restrictions__customer=customer.id))
course_q = Q()
if course_id:
# Third level restriction: may be restricted to a course-code
course_q = Q(
Q(restrictions__course_code__isnull=True)
| Q(restrictions__course_code=course_id))
# apply Q to queryset
return Container.objects.filter(
Q(is_private=False, is_active=True) & org_q & customer_q & course_q)
我建议你创建空的Q
对象并仅在满足条件时替换它们(例如,仅当course_id
为真时);如果条件不满足,那么它只是保持一个空的Q
对象,这意味着一个空的过滤子句。这样.filter()
调用的代码就更简单了。
【讨论】:
一个小问题 - 我需要Q(restrictions__course_code__isnull=True)
来处理所有请求,所以我们不会为这个customer
挑选任何东西,但仅限于不同 @987654331 @ .... 不过我会玩这个.... 看起来不错【参考方案2】:
根据@Ralf 的解决方案,这就是我们所使用的:
def get_containers(customer_id = None, course_id = None):
q_list = Q(is_active=True, restrictions__organisation__isnull=True)
if customer_id:
try:
customers = Customer.objects
customer = customers.get(id=customer_id)
except KeyError:
return None
q_restrict_org = Q(
Q(restrictions__organisation__isnull=True) |
Q(restrictions__organisation=customer.org.id)
)
q_restrict_cust = Q(
Q(restrictions__customer__isnull=True) |
Q(restrictions__customer=customer.id)
)
q_restrict_course = Q(
Q(restrictions__course_code__isnull=True) |
Q(restrictions__course_code=course_id)
)
q_list = (
Q(is_active=True) & Q(
q_restrict_org
& q_restrict_cust
& q_restrict_course
)
)
return Container.objects.filter(q_list)
【讨论】:
【参考方案3】:您发布的错误表明。
'tuple' 表示您从表中获得了一行。 “Q”表示查询,如查询集(多行) 并且您正在尝试合并 2.
很简单,你的问题是q_list查询执行不正确。q_list = restrictions_q.filter(q_list)
这可能是在您声明 q_list
本身时的开头,或者如果您不是指 restrictions
,则必须声明与 q_list
关联的表/查询。
顺便说一句,为什么您在查询中进行了 4 次不同的计算?,您可以简单地过滤所有包含 Q() 的情况。
【讨论】:
以上是关于使用 Q-Objects 在 Django 中动态构建复杂查询的主要内容,如果未能解决你的问题,请参考以下文章
在 django 中使用 mailsnake 使用模板 id 动态创建活动