【问题标题】:How to create an annotation in Django that references two related models如何在 Django 中创建引用两个相关模型的注释
【发布时间】:2019-10-27 09:05:14
【问题描述】:

当一个相关对象上的字段值小于另一个相关对象上的字段值时,我正在尝试向 QuerySet 添加注释,该注释为 True/False。

以下是一些模型示例:

class RobotManager(models.Manager):
    queryset = super(RobotManager, self).get_queryset()
    queryset = queryset.annotate(canteen_empty=UNKNOWN CODE)
    return queryset

class Robot(models.Model):
    # Has some other unrelated stuff
    objects = RobotManager()

class CanteenLevel(models.Model):
    time = models.DateTimeField()
    robot = models.ForeignKey("SomeApp.Robot")
    gallons = models.IntegerField()

class RobotConfiguration(models.Model):
    time = models.DateTimeField()
    robot = models.ForeignKey("SomeApp.Robot")
    canteen_empty_level = models.IntegerField()

使用上述模型,随着机器人的配置或食堂级别的变化,我们会创建新的记录并保存历史记录。

我想做的是向机器人查询集添加一个注释,说明机器人的食堂是否被认为是空的(机器人的最新 CanteenLevel.gallons 小于机器人的最新 Configuration.canteen_empty_level)。

目的是允许使用 QuerySet 中的注释进行这样的语句:

bad_robots = Robot.objects.filter(canteen_empty=True)

我在注释中尝试过这样的事情:

canteen_empty=ExpressionWrapper(CanteenLevel.objects.filter(robot=OuterRef('pk')).order_by('-time').values('gallons')[:1] <= RobotConfiguration.objects.filter(robot=OuterRef('robot')).order_by('-time').values('canteen_empty_level')[:1], output_field=models.BooleanField))

但显然 "

我也试过这个:

canteen_empty=Exists(CanteenLevel.objects.filter(robot=OuterRef('pk')).order_by('-time').values('gallons')[:1].filter(gallons__lte=Subquery(RobotConfiguration.objects.filter(robot=OuterRef('robot')).order_by('-time').values('canteen_empty_level')[:1]))))

但是你不能在获取一个 QuerySet 切片之后进行过滤。

任何帮助将不胜感激!

【问题讨论】:

    标签: django django-annotate


    【解决方案1】:

    我们可以在这里做两个注解:

    from django.db.models import Subquery, OuterRef
    
    latest_gallons = Subquery(CanteenLevel.objects.filter(
        robot=OuterRef('pk')
    ).order_by('-time').values('gallons')[:1])
    
    latest_canteen = Subquery(RobotConfiguration.objects.filter(
        robot=OuterRef('pk')
    ).order_by('-time').values('canteen_empty_level')[:1])

    然后我们可以先用这些注释Robot对象,然后过滤:

    from django.db.models import F
    
    Robot.objects.annotate(
        latest_gallons=latest_gallons,
        latest_canteen=latest_canteen
    ).filter(latest_gallons__lte=F('latest_canteen'))

    这将构造一个如下所示的查询:

    SELECT robot.*,
        (SELECT U0.gallons
         FROM canteenlevel U0
         WHERE U0.robot_id = robot.id
         ORDER BY U0.time DESC
         LIMIT 1) AS latest_gallons,
        (SELECT U0.canteen_empty_level
         FROM robotconfiguration U0
         WHERE U0.robot_id = robot.id
         ORDER BY U0.time DESC
         LIMIT 1) AS latest_canteen
    FROM robot
    WHERE
        (SELECT U0.gallons
         FROM canteenlevel U0
         WHERE U0.robot_id = robot.id
         ORDER BY U0.time DESC
         LIMIT 1
        ) <= (
         SELECT U0.canteen_empty_level
         FROM robotconfiguration U0
         WHERE U0.robot_id = robot.id
         ORDER BY U0.time DESC
         LIMIT 1
        )
    

    但是请注意,如果Robot 没有相关的CanteenLevelRobotConfiguration(其中之一,或两者),则Robot包含在查询集中.

    【讨论】:

    • 非常感谢您快速详细的回复!这绝对能满足我的需要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-08
    • 2011-01-29
    • 2014-11-13
    • 2016-02-27
    • 2016-08-15
    相关资源
    最近更新 更多