Django 1.11 - 嵌套的OuterRef用法

时间:2017-03-30 17:40:01

标签: python sql django orm django-1.11

我最近将Django更新到最新版本1.11rc1,因为那里引入了Subquery功能。

现在,让我们说这是我的用例:我有以下模型 - 用户,组和权限。因此,我有一些可以分组的用户(例如管理员组)和权限 - 这些用户可以执行某些操作(例如,我有用户A,用户B和可以创建新用户的管理员)。我现在要做的是有效地显示所有权限,其中包含许多用户。换句话说,我想创建一个QuerySet,它将返回有关权限的所有信息,并计算每个权限的用户数。解决此问题的第一个显而易见的方法是为Permission模型创建一个get_user_count方法,该方法将从ManyToMany关系返回所有用户,但每个Permission至少需要1个额外查询,这是不可接受的对我来说,因为我打算有很多权限。这是我想要使用Subquery的地方。

所以,澄清一下 - 这就是models.py:

class User(models.Model):
    name = models.CharField(max_length=20)

class Group(models.Model):
    users = models.ManyToManyField(User)

class Permission(models.Model):
    users = models.ManyToManyField(User)
    groups = models.ManyToManyField(Group)

我想创建一个queryset,它将返回包含许多用户的所有Permissions。举个例子,假设我只想包含属于我的组的用户 - 所以我会有这样的事情:

groups = Group.objects.filter(permission=OuterRef('pk'))
users = User.objects.filter(group__in=groups)
queryset = Permission.objects.annotate(
    user_no=Subquery(users.annotate(c=Count('*')).values('c'))
)

这里的问题是我的OuterRef无法解析为“子查询的过滤器过滤器”中使用的:

This queryset contains a reference to an outer query and may only be used in a subquery.

虽然,当我使用另一个子查询来获取组时:

groups = Group.objects.filter(permission=OuterRef(OuterRef('pk')))
users = User.objects.filter(group__in=Subquery(groups))
queryset = Permission.objects.annotate(
    user_no=Subquery(users.annotate(c=Count('*')).values('c'))
)

我在第一行收到错误:

int() argument must be a string, a bytes-like object or a number, not 'OuterRef'

其余行无关紧要,对错误没有影响。奇怪的是,文档中出现了完全相同的语法:https://docs.djangoproject.com/en/dev/ref/models/expressions/#django.db.models.OuterRef

问题是:我做错了什么?或者如何以其他方式实现我想要的(虽然有效)?

1 个答案:

答案 0 :(得分:1)

嗯,这是Django中的一个错误:https://github.com/django/django/pull/9529

我通过使用注释排除doulbe-depth(OuterRef(OuterRef('pk')))来修复它:

return self.annotate(category=Subquery(
    # this is the "inner" subquery which is now just an annotated variable `category`
    Category.objects.filter(offer=OuterRef('pk'))[:1].values('pk')
)).annotate(fs=Subquery(
    # this is the "outer" subquery; instead of using subquery, I just use annotated `category` variable
    Category.objects.filter(pk=OuterRef('category')).values('slug')
))

希望有所帮助:)