Django Q查询&在同一字段上?

时间:2019-03-28 14:57:44

标签: sql django django-queryset django-q

这是我的模特:

class Event(models.Model):
    user = models.ForeignKey(User, blank=True, null=True, db_index=True)
    name = models.CharField(max_length = 200, db_index=True)
    platform = models.CharField(choices = (("ios", "ios"), ("android", "android")), max_length=50)

class User(AbstractUser):
    email = models.CharField(max_length=50, null=False, blank=False, unique=True)

Event就像一个分析事件,因此如果一个用户登录,我很有可能会为一个用户举办多个事件,其中一些与platform=ios,另一些与platform=android在多个设备上。我想查询一下有多少用户同时拥有ios和android设备。所以我写了这样的查询:

User.objects.filter(Q(event__platform="ios") & Q(event__platform="android")).count()

哪个返回0个结果。我知道这是不正确的。然后我以为我会尝试只查询iOS用户:

User.objects.filter(Q(event__platform="ios")).count()

哪个返回了6,717,622个结果,这是意外的,因为我只有39,294个用户。我猜它不是在计算用户,而是在计算Event实例,这对我来说似乎是错误的行为。有人对这个问题有见识吗?

2 个答案:

答案 0 :(得分:2)

您可以改用注释:

django.db.models import Count

User.objects.all().annotate(events_count=Count('event')).filter(events_count=2)

因此它将过滤掉具有两个事件的所有用户。

您还可以使用链接过滤器:

User.objects.filter(event__platform='android').filter(event__platform='ios')

第一个过滤器将获取所有具有android平台的用户,第二个过滤器将获取也具有iOS平台的用户。

答案 1 :(得分:0)

这通常是针对具有两个或多个与子对象相关的条件的查询集的答案。

解决方案:即使没有任何连接,也可以使用带有两个子查询的简单解决方案:

base_subq = Event.objects.values('user_id').order_by().distinct()
user_qs = User.objects.filter(
    Q(pk__in=base_subq.filter(platform="android")) &
    Q(pk__in=base_subq.filter(platform="ios"))
)

如果模型Event具有默认顺序(请参见有关distinct()方法的文档中的内容),方法.order_by()很重要。


注释

验证将执行的唯一SQL请求:(通过删除“ app_”前缀进行简化。)

>>> print(str(user_qs.query))
SELECT user.id, user.email FROM user WHERE (
    user.id IN (SELECT DISTINCT U0.user_id FROM event U0 WHERE U0.platform = 'android')
    AND
    user.id IN (SELECT DISTINCT U0.user_id FROM event U0 WHERE U0.platform = 'ios')
)
  • 之所以使用功能Q()是因为无法在同一pk__in中重复相同的条件参数(filter()),但是也可以使用链接过滤器:{{1} }。 (过滤条件的顺序并不重要,SQL Server优化程序估计的首选项对此没有影响。)
  • 临时变量.filter(...).filter(...)是一个“别名”查询集,仅用于不重复表达式的相同部分,而永远不会单独对其进行求值。
  • 用户(父)和事件(子)之间的一次联接不是问题,也可以使用一个子查询来解决,但是可以通过事件和事件(一个重复的子对象或两个子对象联接)进行联接对象)在任何情况下都应通过子查询来避免。两个子查询很容易阅读,可以证明两个过滤条件的对称性。

另一个带有两个嵌套子查询的解决方案,如果我们知道一个子查询(放在最里面)的限制条件比另一个带有大量子查询的必要子查询的约束条件要严格得多,则这种非对称解决方案会更快。结果。 (例如,如果有大量的Android用户将是巨大的话)

base_subq

验证如何将其编译为SQL :(通过删除ios_user_ids = (Event.objects.filter(platform="ios") .values('user_id').order_by().distinct()) user_ids = (Event.objects.filter(platform="android", user_id__in=ios_user_ids) .values('user_id').order_by().distinct()) user_qs = User.objects.filter(pk__in=user_ids) 前缀和app_再次简化。)

"

(这些解决方案在旧版Django中也有效,例如1.8。自Django 1.11起,针对更复杂的情况,存在一个特殊的子查询函数Subquery(),但对于这个简单的问题我们并不需要它。)