Могу ли я повторно использовать экземпляр output_field в Django ORM, или мне всегда следует создавать дубликат?

Вопрос или проблема

У меня есть кодовая база на Django, которая выполняет много функций ORM Case/When/ExpressionWrapper/Coalesce/Cast, и некоторые из них иногда требуют поле в качестве аргумента – output_field.

from django.db.models import FloatField, F
some_param1=Sum(F('one_value')*F('second_value'), output_field=FloatField())
some_param2=Sum(F('one_value')*F('second_value'), output_field=FloatField())
some_param3=Sum(F('one_value')*F('second_value'), output_field=FloatField())
some_param4=Sum(F('one_value')*F('second_value'), output_field=FloatField())
some_param5=Sum(F('one_value')*F('second_value'), output_field=FloatField())

Иногда я задумываюсь, почему я всегда создаю один и тот же экземпляр любого подкласса Field снова и снова. Есть ли какая-то разница, если я просто передам один экземпляр и разделю его между выражениями? Например:

from django.db.models import FloatField, F

float_field = FloatField()

some_param1=Sum(F('one_value')*F('second_value'), output_field=float_field)
some_param2=Sum(F('one_value')*F('second_value'), output_field=float_field)
some_param3=Sum(F('one_value')*F('second_value'), output_field=float_field)
some_param4=Sum(F('one_value')*F('second_value'), output_field=float_field)
some_param5=Sum(F('one_value')*F('second_value'), output_field=float_field)

Я не смог найти это в документации, и исходный код плохо документирован в отношении этого параметра.

П.С. Пример вымышленный, просто представьте собой большую функцию аннотирования, которая выполняет много обработки с использованием Case/When/ExpressionWrapper/Coalesce/Cast и имеет много дублирующихся экземпляров Field в качестве output_field.

Вы можете повторно использовать поле. Использование этого output_field=… [Django-doc] служит двум целям:

  1. тип иногда требует специального форматирования, обычно для GIS колонок, так как точка, полигоны и т.д. должны быть преобразованы в текст, чтобы Django мог их понять; и
  2. чтобы знать, какие преобразования и фильтры могут быть применены к нему.

Действительно, если мы используем:

queryset = queryset.annotate(
    some_param1=Sum(
        F('one_value') * F('second_value'), output_field=CharField()
    )
)

то Django предположит, что some_param1 является CharField (здесь это не имеет большого смысла), и таким образом вы можете использовать:

queryset.filter(some_param1__lower='a')

поскольку __lower определен как фильтр для CharField. Но для FloatField это не имеет большого смысла.

Но поле не специализировано и не изменено. Это больше похоже на “сигнальный” объект для указания, что с ним можно сделать.

Тем не менее, я не вижу много причин для преобразования кода с целью предотвратить создание FloatField. Если мы используем %timeit, мы получаем:

In [1]: %timeit FloatField()
3.82 µs ± 379 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

Таким образом, создание занимает примерно 3.82 микросекунды. Обычно представление имеет гораздо больше работы, чем это, поэтому написание более эффективного запроса или сохранение раундтрип до базы данных (очень) вероятно превзойдет любую оптимизацию, связанную с сохранением FloatField на несколько порядков.

Ответ или решение

В контексте использования Django ORM, вы задаете важный вопрос о повторном использовании экземпляра output_field при работе с выражениями, такими как Sum, Case, When, и другими. Давайте подробно разберем этот вопрос и обсудим, стоит ли создавать дубликаты или можно безопасно переиспользовать один и тот же экземпляр.

Понимание output_field

В Django ORM параметр output_field, передаваемый в функции агрегирования и аннотирования, играет важную роль. Он предоставляет информацию о типе данных, которые возвращает выражение, и это позволяет ORM делать правильные преобразования. В частности, это:

  1. Определение типа данных: output_field помогает Django понимать, какие преобразования или фильтры могут применяться к результатам выражений. Например, если вы укажете поле типа CharField, это позволит вам использовать соответствующие операции и фильтры, которые применимы только к строкам.

  2. Форматирование данных: В некоторых случаях, таких как работа с географическими типами данных, правильное указание поля обеспечивает преобразование результатов в соответствующий формат.

Можем ли мы переиспользовать экземпляр?

Отвечая на ваш вопрос: да, вы можете переиспользовать экземпляр output_field, и это не повлияет на корректность работы вашего кода. На практике вы можете создать один экземпляр, а затем многократно использовать его в различных выражениях. Вот пример, который иллюстрирует это:

from django.db.models import FloatField, F

float_field = FloatField()

some_param1 = Sum(F('one_value') * F('second_value'), output_field=float_field)
some_param2 = Sum(F('one_value') * F('second_value'), output_field=float_field)
some_param3 = Sum(F('one_value') * F('second_value'), output_field=float_field)

Потенциальные преимущества и недостатки

  1. Преимущества:

    • Упрощение кода: Вы значительно сократите количество строк кода, что улучшит его читаемость и поддержку.
    • Избежание дубликации: Переиспользование предотвращает появление многократных переменных, которые могут сбить с толку при большом количестве аннотаций.
  2. Недостатки:

    • Необходимость тестирования: В то время как переиспользование output_field в общем безопасно для большинства типов полей, важно удостовериться, что используемые в вашем проекте поля действительно совместимы с вашими выражениями.

Заключение

В целом, рекомендуется использовать один и тот же экземпляр output_field, когда это возможно. Это повысит читаемость вашего кода и уменьшит избыточность, не отрицая при этом работоспособности выражений. Однако, всегда стоит внимательно следить за тем, как разные типы полей взаимодействуют в конкретных контекстах, и тестировать ваш код, чтобы избежать неожиданных ошибок.

Если у вас есть дополнительные вопросы или вам нужна помощь с другими аспектами Django ORM, не стесняйтесь обращаться!

Оцените материал
Добавить комментарий

Капча загружается...