Django过滤同一外键对象的两个字段
Posted
技术标签:
【中文标题】Django过滤同一外键对象的两个字段【英文标题】:Django filter on two fields of the same foreign key object 【发布时间】:2014-09-29 03:04:00 【问题描述】:我有一个类似这样的数据库架构:
class User(models.Model):
… (Some fields irrelevant for this query)
class UserNotifiy(models.Model):
user = models.ForeignKey(User)
target = models.ForeignKey(<Some other Model>)
notification_level = models.SmallPositivIntegerField(choices=(1,2,3))
现在我想查询所有拥有特定目标的 UserNotify 对象和至少特定通知级别(例如 2)的用户。
如果我这样做:
User.objects.filter(usernotify__target=desired_target,
usernotify__notification_level__gte=2)
我得到所有具有指定目标的 UserNotify 对象和至少一个 Notification_level 大于或等于 2 的 UserNotify 对象的用户。但是,这两个 UserNotify 对象不必相同。
我知道我可以这样做:
user_ids = UserNotify.objects.filter(target=desired_target,
notification_level__gte=2).values_list('user_id', flat=True)
users = User.objects.filter(id__in=user_ids).distinct()
但这一步对我来说似乎太过分了,我相信它会执行两个查询。
有没有办法通过单个查询来解决我的问题?
【问题讨论】:
我不确定我是否理解您的问题,您说您想要该查询并且该查询正在运行,具体问题是什么?你是什么意思相同的对象? 【参考方案1】:实际上,鉴于usernotify
不是User
的有效字段名称,我不知道如何运行第一个查询。
您应该从 UserNotify
开始,就像您在第二个示例中所做的那样:
UserNotify.objects.filter(
target=desired_target,
notification_level__gte=2
).select_related('user').values('user').distinct()
【讨论】:
'field' usernotify 用于反向查找外键关系,就像在 Python 代码中可以使用 usernotify_set。 您提出的代码只给了我用户的 id,与我提出的解决方案相比没有任何优势,因为我仍然需要启动一个额外的查询来获取用户对象的信息。跨度> @TimSchneider 如果您需要其他(给定)字段并且您正在使用 postgres,请查看 trecouvr 答案。否则,您必须坚持使用第二个查询,除非您想纯粹在 python 中获得不同的用户。【参考方案2】:我一直在寻找这种行为,但我从未找到比您描述的更好的方法(为用户 ID 创建查询并将其注入用户查询)。请注意,这还不错,因为如果您的数据库支持子查询,您的代码应该只触发一个由查询和子查询组成的请求。
但是,如果您只需要用户对象中的特定字段(例如 first_name),您可以尝试
qs = (UserNotify.objects
.filter(target=desired_target, notification_level__gte=2)
.values_list('user_id', 'user__first_name')
.order_by('user_id')
.distinct('user_id')
)
【讨论】:
这个语法只有 Postgres 支持 是的,我刚刚注意到它......当您使用 distinct(*fields) 语法时,您还必须提供一个 order_by,其中包含与不同字段匹配的第一个字段。 (docs.djangoproject.com/en/1.7/ref/models/querysets/…) 看来你是对的。我为这个问题提出的解决方案只创建一个带有一个子查询的查询。由于没有子查询似乎没有更好的解决方案,我想这是要走的路。感谢您的帮助。【参考方案3】:我不确定我是否理解你的问题,但是:
class User(models.Model):
… (Some fields irrelevant for this query)
class UserNotifiy(models.Model):
user = models.ForeignKey(User, related_name="notifications")
target = models.ForeignKey(<Some other Model>)
notification_level = models.SmallPositivIntegerField(choices=(1,2,3))
然后
users = User.objects.select_related('notifications').filter(notifications__target=desired_target,
notifications__notification_level__gte=2).distinct('id')
for user in users:
notifications = [x for x in user.notifications.all()]
我现在手边没有我的 vagrant box,但我相信这应该可以。
【讨论】:
以上是关于Django过滤同一外键对象的两个字段的主要内容,如果未能解决你的问题,请参考以下文章
如果只有另一个模型的字段包含某个值并且这两个模型具有外键,我如何过滤模型的对象
Django的管理页面怎么显示和过滤另外一个表的字段,非外键