【问题标题】:Complex queryset with annotate across different models具有跨不同模型注释的复杂查询集
【发布时间】:2014-06-03 23:39:42
【问题描述】:

我正在使用 Django 1.6 并有以下模型(为了便于阅读,稍微简化了一些):

class Person(models.Model):
    name = models.CharField(max_length=128)

class Room(models.Model):
    max_persons = models.SmallIntegerField()
    name = models.CharField(max_length=30, unique=True)

class Stay(models.Model):
    person = models.ForeignKey(Person)
    STAY_TYPE = (
        (1, 'Type 1'),
        (2, 'Type 2'),
        (3, 'Type 3'),
    )
    stay_type = models.CharField(max_length=1, choices=STAY_TYPE)

class Hospitalization(models.Model):
    stay = models.ForeignKey(Stay)
    date_in = models.DateField()
    date_out = models.DateField(blank=True, null=True)
    room = models.ForeignKey(Room)

使用queryset(在经理中),我想获取给定日期的所有可用房间,即不满足max_persons 的位置。

到目前为止,我尝试过使用QRoom.objects.filter(hospitalization__date_out__lt="2014-04-25") 之类的东西,但我无法弄清楚max_persons 的比较。

你有什么想法吗?

编辑

与每个房间相关的 Person 对象的数量代表了该房间中的人数。

因此,根据 Karl 的以下建议,我尝试了以下方法:

  • Room.objects.filter(hospitalization__date_out__lte="2014-04-25").annotate(num_persons=hospitalization_set__stay__person.Count()).exclude(num_persons__lte=max_persons)
    • 产量NameError: name 'hospitalization_set__stay__person' is not defined
  • Room.objects.filter(hospitalization__date_out__lte="2014-04-25").annotate(num_persons=Count('hospitalization_set__stay__person')).exclude(num_persons__lte=max_persons)
    • 产量FieldError: Cannot resolve keyword 'hospitalization_set' into field.
  • Room.objects.filter(hospitalization__date_out__lte="2014-04-25").annotate(num_persons=Count('hospitalization__stay__person')).exclude(num_persons__lte=F('max_persons'))
    • 没有任何结果 ([]),而根据我当前的数据库,我预计至少有三个结果。
    • 删除.exclude() 时,我仍然没有得到任何结果。看起来我正在使用的 .annotate() 做错了。

【问题讨论】:

    标签: django django-queryset django-managers


    【解决方案1】:

    我相信类似于Room.objects.filter(hospitalization__date_out="2014-04-25", max_persons__lt=x) 的东西应该可以解决问题。请记住,您可以在过滤器操作中有多个声明,并且您还可以链接排除和过滤器。这不会增加数据库活动; queryset 过滤器等仅在评估 queryset 本身时执行。见The Django Docs on making queries

    编辑: 这实际上比我最初想象的要复杂一些,但我相信我已经解决了(在导入你的模型声明并进行 shell 会话之后!)

    rooms = Room.objects.filter(DATE FILTERING HERE).annotate(num_persons=Count('hospitalization__room__id')).filter(num_persons__lt=F('max_persons')).count()
    

    将为您提供num_persons(我们上面的注释变量)小于max_persons的房间数量,并预先进行日期过滤。

    请记住包含所需的导入: from django.db.models import Count from django.db.models import F

    【讨论】:

    • 嗯,但我应该以某种方式使用相同的查询集得到“x”,不是吗?这将如何运作?
    • 这取决于 x 应该具有的值。我假设与每个房间相关的 Person 对象的数量将代表该房间的人数?在这种情况下,您将使用这些关系的计数来注释查询集。我会亲自修改您的模型声明,以包含“Person”和“Room”之间更简单的关系。事实上,我相信类似的东西可以工作(未经测试): Room.objects.filter(hospitalization__date_out="2014-04-25").annotate(num_persons=Hospitalization_set__stay__person.Count()).exclude(num_persons__lte=max_persons)
    • 正确,与每个房间相关的 Person 对象的数量将代表该房间的人数。我一直在考虑修改我的模型以简化事情,但问题是在一次住宿中,人的房间可以改变;我会补充我的问题。我已经尝试过您的查询(感谢您的帮助!!),但无法正常工作。我似乎无法在查询中使用反向 hospitalization_set。我试图通过将路径作为参数添加到聚合计数函数来解决这个问题(不确定是否可以),但现在我无法进行“排除”比较。
    • (修复了exclude使用F对象的比较-调整了问题)
    • 我完全忘记在这里添加评论@SaeX ...请参阅上面对我的回答的编辑。该查询应该为您提供您正在寻找的响应,而无需修改您的模型声明。
    猜你喜欢
    • 2014-12-22
    • 2013-03-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    相关资源
    最近更新 更多