Django QuertySet.annotate () получил невыражение - как добавить производное поле на основе поля модели? - PullRequest
0 голосов
/ 17 апреля 2020

Первый раз с Django. Попытка добавить аннотацию в набор запросов:

class EnrollmentManager(models.Manager.from_queryset(EnrollmentCustomQuerySet)):
  COURSE_DURATION = datetime.timedelta(days=183)

  def get_queryset(self):
      """Overrides the models.Manager method"""
      lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
      qs = super(EnrollmentManager, self).get_queryset().annotate( \
              is_expired=(Value(True)), output_field=models.BooleanField())
      return qs

В данный момент я просто пытаюсь добавить дополнительное «вычисленное» поле в возвращенный набор запросов, которое жестко закодировано в True, а атрибут / поле должен быть названным is_expired.

Если я смогу заставить это работать, тогда Value (True) должен быть производным значением, основанным на этом выражении:

F('enrolled') < lookback

Но так как "зарегистрирован" такое поле базы данных и вычисляется lookback, как я смогу это сделать?

Примечание

Я пробовал это, которое выполняется без выдачи ошибки:

qs = super(EnrollmentManager, self).get_queryset().annotate( \
         is_expired=(Value(True, output_field=models.BooleanField())))

и в оболочке я вижу это:

Enrollment.objects.all()[0].is_expired -> returns True

и я могу добавить его в сериализатор:

class EnrollmentSerializer(serializers.ModelSerializer):
  is_active = serializers.SerializerMethodField()
  is_current = serializers.SerializerMethodField()
  is_expired = serializers.SerializerMethodField()
  COURSE_DURATION = datetime.timedelta(days=183)

  class Meta:
    model = Enrollment
    fields = ('id', 'is_active', 'is_current', 'is_expired')

  def get_is_expired(self, obj):
    return obj.is_expired

Так что это возможно. ... но как я могу заменить свое жестко запрограммированное «Истина» на вычисление?

ОБНОВЛЕНИЕ

При чтении документации в нем говорится:

"Аннотирует каждый объект в QuerySet с предоставленным списком выражений запроса. Выражение может быть простым значением, ссылкой на поле в модели (или любых связанных моделях) или агрегированным выражением (средние, суммы и т. Д. c.), Которое было вычислено по объектам, связанным с объекты в QuerySet. "

Простое значение - значит, не простое значение COMPUTED?

Это заставляет меня думать, что это невозможно ...

1 Ответ

1 голос
/ 17 апреля 2020

Это похоже на довольно хороший вариант использования выражения Case. Я предлагаю как можно лучше познакомиться с этими инструментами выражения , они очень полезны!

Я не проверял это, но оно должно работать. Я предполагаю, что enrolled - это дата-время с учетом tz, когда они впервые зарегистрировались ...

from django.db.models import Case, When, Value

def get_queryset(self):
    """Overrides the models.Manager method"""
    lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
    qs = super(EnrollmentManager, self).get_queryset().annotate(
        is_expired=Case(
            When(
                enrolled__lt=lookback,
                then=Value(True)
            ),
            default=Value(False),
            output_field=models.BooleanField()
        )
    )

Вам также не нужно предварительно вычислять переменную просмотра. Проверьте ExpressionWrappers и этот ответ StackOverflow , который обращается к этому.

ExpressionWrapper(
    TruncDate(F('date1')) + datetime.timedelta(days=365),
    output_field=DateField(),
)
...