Как создать аннотацию в Django, которая ссылается на две связанные модели - PullRequest
0 голосов
/ 12 июня 2019

Я пытаюсь добавить аннотацию к 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, мы создаем новые записи и сохраняем исторические данные.

Что я хотел бы сделать, так это добавить аннотацию к роботу QuerySet, в которой будет указано, считается ли столовая робота пустой (последний 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))

Но, очевидно,Оператор "<=" не разрешен. </p>

Я также пробовал это:

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.

Любая помощь будет оценена!

1 Ответ

1 голос
/ 12 июня 2019

Мы можем сделать две аннотации здесь:

from django.db.models import Subquery, OuterRef

latest_gallons = <b>Subquery(</b>CanteenLevel.objects.filter(
    robot=OuterRef('pk')
).order_by('-time').values('gallons')[:1]<b>)</b>

latest_canteen = <b>Subquery(</b>RobotConfiguration.objects.filter(
    robot=OuterRef('pk')
).order_by('-time').values('canteen_empty_level')[:1]<b>)</b>

тогда мы можем сначала аннотировать Robot объекты этими объектами и фильтровать:

from django.db.models import <b>F</b>

Robot.objects.annotate(
    latest_gallons=latest_gallons,
    latest_canteen=latest_canteen
).filter(<b>latest_gallons__lte=F('latest_canteen')</b>)

Это создаст запрос, который выглядит следующим образом:

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 не имеет связанных CanteenLevel или RobotConfiguration (одного из них или обоих), то Robot не будет не включаться в набор запросов.

...