从其他 Q() 对象构建 Django Q() 对象,但具有交叉上下文关系
Building Django Q() objects from other Q() objects, but with relation crossing context
我经常发现自己在我的 Django 应用程序中多次编写相同的条件。我通常会将它封装在一个 return 是 Django Q() 对象的函数中,这样我就可以在一个地方维护标准。
我将在我的代码中做这样的事情:
def CurrentAgentAgreementCriteria(useraccountid):
'''Returns Q that finds agent agreements that gives the useraccountid account current delegated permissions.'''
AgentAccountMatch = Q(agent__account__id=useraccountid)
StartBeforeNow = Q(start__lte=timezone.now())
EndAfterNow = Q(end__gte=timezone.now())
NoEnd = Q(end=None)
# Now put the criteria together
AgentAgreementCriteria = AgentAccountMatch & StartBeforeNow & (NoEnd | EndAfterNow)
return AgentAgreementCriteria
这使得我不必多次考虑数据库模型,并且我可以结合这些函数的 return 值来构建更复杂的标准。到目前为止效果很好,并且在数据库模型发生变化时已经为我节省了时间。
当我开始结合这些函数的标准时,我已经意识到,Q() 对象本质上与正在调用的对象 .filter() 的类型相关联。这就是我所期望的。
我偶尔会发现自己想使用我的一个函数中的 Q() 对象来构造另一个 Q 对象 ,该对象旨在过滤不同但相关的模型实例 .
让我们用一个 simple/contrived 例子来说明我的意思。 (这很简单,通常这不值得开销,但请记住,我在这里使用一个简单的示例来说明我的应用程序中更复杂的部分。)
假设我有一个函数 return 是一个 Q() 对象,它可以找到所有 Django 用户,其用户名以 'a':
开头
def UsernameStartsWithAaccount():
return Q(username__startswith='a')
假设我有一个相关模型,它是一个用户配置文件,其中包含设置,包括他们是否需要我们的电子邮件:
class UserProfile(models.Model):
account = models.OneToOneField(User, unique=True, related_name='azendalesappprofile')
emailMe = models.BooleanField(default=False)
假设我想找到所有用户名以 'a' 开头的用户配置文件,并想用它们向他们发送一些电子邮件通讯。我可以轻松地为后者编写一个 Q() 对象:
wantsEmails = Q(emailMe=True)
但发现自己想要为前者做这样的事情:
startsWithA = Q(account=UsernameStartsWithAaccount())
# And then
UserProfile.objects.filter(startsWithA & wantsEmails)
不幸的是,这不起作用(当我尝试时它生成无效的 PSQL 语法)。
换句话说,我正在寻找一种类似于 Q(account=Q(id=9))
的语法,它 return 与 Q(account__id=9)
.
的结果相同
所以,由此产生了几个问题:
- 是否有 Django Q() 对象的语法允许您向它们添加 "context" 以允许它们跨越您所在的模型的关系边界 运行 .filter() ?
- 如果不是,这在逻辑上可能吗? (因为当我想做类似
Q(account=Q(id=9))
的事情时我可以写 Q(account__id=9)
它 看起来 就像它会那样)。
也许有人提出了更好的建议,但我最终手动将上下文传递给此类函数。我不认为有一个简单的解决方案,因为您可能需要调用一整条相关的 table 链才能到达您的领域,例如 table1__table2__table3__profile__user__username
,您怎么猜?用户 table 也可以链接到 table2
,但在这种情况下你不需要它,所以我认为你无法避免手动设置路径。
您还可以将字典传递给 Q()
,将列表或字典传递给 filter()
函数,这比使用关键字参数和应用 &
更容易使用。
def UsernameStartsWithAaccount(context=''):
field = 'username__startswith'
if context:
field = context + '__' + field
return Q(**{field: 'a'})
然后,如果您只需要 AND
您的条件,您可以将它们组合成一个列表并传递给过滤器:
UserProfile.objects.filter(*[startsWithA, wantsEmails])
我经常发现自己在我的 Django 应用程序中多次编写相同的条件。我通常会将它封装在一个 return 是 Django Q() 对象的函数中,这样我就可以在一个地方维护标准。
我将在我的代码中做这样的事情:
def CurrentAgentAgreementCriteria(useraccountid):
'''Returns Q that finds agent agreements that gives the useraccountid account current delegated permissions.'''
AgentAccountMatch = Q(agent__account__id=useraccountid)
StartBeforeNow = Q(start__lte=timezone.now())
EndAfterNow = Q(end__gte=timezone.now())
NoEnd = Q(end=None)
# Now put the criteria together
AgentAgreementCriteria = AgentAccountMatch & StartBeforeNow & (NoEnd | EndAfterNow)
return AgentAgreementCriteria
这使得我不必多次考虑数据库模型,并且我可以结合这些函数的 return 值来构建更复杂的标准。到目前为止效果很好,并且在数据库模型发生变化时已经为我节省了时间。
当我开始结合这些函数的标准时,我已经意识到,Q() 对象本质上与正在调用的对象 .filter() 的类型相关联。这就是我所期望的。
我偶尔会发现自己想使用我的一个函数中的 Q() 对象来构造另一个 Q 对象 ,该对象旨在过滤不同但相关的模型实例 .
让我们用一个 simple/contrived 例子来说明我的意思。 (这很简单,通常这不值得开销,但请记住,我在这里使用一个简单的示例来说明我的应用程序中更复杂的部分。)
假设我有一个函数 return 是一个 Q() 对象,它可以找到所有 Django 用户,其用户名以 'a':
开头def UsernameStartsWithAaccount():
return Q(username__startswith='a')
假设我有一个相关模型,它是一个用户配置文件,其中包含设置,包括他们是否需要我们的电子邮件:
class UserProfile(models.Model):
account = models.OneToOneField(User, unique=True, related_name='azendalesappprofile')
emailMe = models.BooleanField(default=False)
假设我想找到所有用户名以 'a' 开头的用户配置文件,并想用它们向他们发送一些电子邮件通讯。我可以轻松地为后者编写一个 Q() 对象:
wantsEmails = Q(emailMe=True)
但发现自己想要为前者做这样的事情:
startsWithA = Q(account=UsernameStartsWithAaccount())
# And then
UserProfile.objects.filter(startsWithA & wantsEmails)
不幸的是,这不起作用(当我尝试时它生成无效的 PSQL 语法)。
换句话说,我正在寻找一种类似于 Q(account=Q(id=9))
的语法,它 return 与 Q(account__id=9)
.
所以,由此产生了几个问题:
- 是否有 Django Q() 对象的语法允许您向它们添加 "context" 以允许它们跨越您所在的模型的关系边界 运行 .filter() ?
- 如果不是,这在逻辑上可能吗? (因为当我想做类似
Q(account=Q(id=9))
的事情时我可以写Q(account__id=9)
它 看起来 就像它会那样)。
也许有人提出了更好的建议,但我最终手动将上下文传递给此类函数。我不认为有一个简单的解决方案,因为您可能需要调用一整条相关的 table 链才能到达您的领域,例如 table1__table2__table3__profile__user__username
,您怎么猜?用户 table 也可以链接到 table2
,但在这种情况下你不需要它,所以我认为你无法避免手动设置路径。
您还可以将字典传递给 Q()
,将列表或字典传递给 filter()
函数,这比使用关键字参数和应用 &
更容易使用。
def UsernameStartsWithAaccount(context=''):
field = 'username__startswith'
if context:
field = context + '__' + field
return Q(**{field: 'a'})
然后,如果您只需要 AND
您的条件,您可以将它们组合成一个列表并传递给过滤器:
UserProfile.objects.filter(*[startsWithA, wantsEmails])