Django过滤同一个外键对象的两个字段

时间:2014-08-06 08:48:24

标签: django foreign-key-relationship django-queryset

我有一个类似于此的数据库模式:

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()

但这似乎对我来说太过分了,我相信它会执行两个查询。

有没有办法用单个查询解决我的问题?

3 个答案:

答案 0 :(得分:0)

我不确定我是否理解你的问题,但是:

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()]

我现在没有方便使用我的流浪盒,但我相信这应该有效。

答案 1 :(得分:0)

我一直在寻找这种行为,但我从来没有找到比你描述的更好的方法(为用户ID创建查询并将其注入用户查询)。请注意,这并不错,因为如果您的数据库支持子查询,您的代码应该只触发由查询和子查询组成的一个请求。

但是,如果您只需要User对象中的特定字段(例如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')
)

答案 2 :(得分:0)

实际上,我不知道如何运行第一个查询,因为usernotify不是User的有效字段名称。

你应该像在第二个例子中那样从UserNotify开始:

UserNotify.objects.filter(
    target=desired_target,
    notification_level__gte=2
).select_related('user').values('user').distinct()