Django ломает длинные имена поиска по запросам

Предположим, что существует строка кода для выполнения запроса с использованием ORM Django, который содержит очень длинное имя поиска:

QuerySet.filter(myfk__child__onetoone__another__manytomany__relation__monster__relationship__mycustomlookup=':P')

Я хотел бы сломать строку, чтобы следовать pep8, особенно Ограничение 79 символов

Я знаю, что мы можем сделать что-то вроде:

   QuerySet.filter(
      **{
        'myfk__child__onetoone__another' 
        '__manytomany__relation__monster' 
        '__relationship__mycustomlookup': ':P'
      }
    )

Но мне интересно, есть ли другой, возможно, более пифонический/принятый способ?

Ответ 1

Возможно, использование LOOKUP_SEP для объединения имен поиска немного более доступно?

from django.db.models.constants import LOOKUP_SEP

lookup = LOOKUP_SEP.join(['myfk', 'child', 'onetoone', 'another', 'manytomany',
                          'relation', 'monster', 'relationship',
                          'mycustomlookup'])

QuerySet.filter(**{lookup:':P'})

Ответ 2

pep8 говорит:

Предпочтительным способом обертывания длинных строк является использование продолжения строки под Python в круглых скобках, скобках и фигурных скобках.

Что вы сделали, поэтому я думаю, что у вас есть самый пифонический (или, по крайней мере, самый pep8ic) способ сделать это.

Ответ 3

Репозиторий Django project настраивает символы max-line-length до 119 в .editorconfig и setup.cfg (см. выделенную строку в обеих ссылках). Все современные проверки кода (pycodestyle, pylint, pyflakes, pep8) и редакторы понимают эту конфигурацию и принимают ее вместо 79 ch.

Отражение: я также предпочитаю писать 79 ch в основном, потому что обычно лучше читаем, но в вашем случае длина строки 119 строк определенно более читаема, чем разделение имени переменной на короткие строки на **{...}. Очень короткие строки важны, если Python используется в одном терминале, например, в установщике Linux script. Для Django у вас обычно есть гораздо лучший локальный псевдотерминал или SSH-терминал. Github поддерживает 119 символов в каждом представлении. Возможно, графические инструменты, используемые для разрешения конфликтов слияния бок о бок, могут потребовать прокрутки по горизонтали на каком-то мониторе. С другой стороны, автоматизация слияния или дифферирования может выходить из строя чаще, потому что повторяющиеся последовательности тех же строк создаются только путем разрыва строки из-за правила 79 ch.

Ответ 4

РЕДАКТИРОВАТЬ (здесь более упрощенный ответ. Оригинальный подробный ответ ниже под строкой.)

Я написал модуль django_dot_filter.py, который помогает писать фильтры запросов более естественно и читабельны. Выражение начинается с символа V и имен, разделенных точками:

from django_dot_filter import V

QuerySet.filter(V.myfk.child.onetoone.another.manytomany
                .relation.monster.relationship
                .mycustomlookup == ':P')

Я прочитал его как "это неизвестное V допустимое" с полями... поэтому я использую букву V. Этот класс действительно является только символом, за которым могут следовать точки, методы, операторы и т.д., Все разделенные . вместо __.

Поддерживаются стандартные операторы чтения, такие как <, <=, == или !=, а также круглые скобки и логические операторы &, |, ~.

Queryset.filter((V.some_related.my_field >= 10)
                | ~V.field_x.startswith('Y') & (V.date_field.year() == 2017)
                & V.price.range(10, 100))

Каждый поиск может быть написан классическим способом, как атрибут V.date_field.year == 2017, или как метод V.date_field.year() == 2017. Многие поисковые запросы гораздо читабельны как метод с аргументом V.my_field.regex(r'^[abc]') вместо my_field__regex=value. Мне гораздо удобнее видеть соглашение, что .date() - это метод поиска, но .date - это поле.

Это не волшебство. Только метод с аргументами или оператор отношения - это каждый раз в последней части поиска. Метод без аргументов - это только символ, который является поиском. Всегда следует что-то со значением. Выражения компилируются в выражения Q, включая булевы выражения. Они могут быть легко использованы в похожих проектах, сохраняются в переменных и т.д., Тогда как условия exclude(..) вместо отсутствующего оператора != менее многократно используются.

(В настоящее время не известны никакие неподдерживаемые функции. Некоторые тесты были написаны. Если я получаю достаточную обратную связь, это может быть пакет. Это немного более многословный, чем классический хороший name=value, подходящий для простых случаев.



Другой ответ, если вам нравятся читаемые фильтры с возможными длинными цепочками связанных полей, даже если они сложны.

Я написал простой модуль django_dot_filter.py, что позволяет использовать точечный синтаксис для полей связанных моделей и используют операторы ==,! =, <, < =, > , >= для условий. Он может использовать побитовые операторы ~ | и в качестве булевых операторов, аналогично Q объектов, но из-за приоритета операторов сравнение должно быть заключено в круглые скобки. Он вдохновлен синтаксисом, используемым в SQLAlchemy и Pandas.

строка doc:

class V(...):
    """
    Syntax suger for more readable queryset filters with "." instead "__"

    The name "V" can be understand like "variable", because a shortcut for
    "field" is occupied yet.
    The syntax is very similar to SQLAlchemy or Pandas.
    Operators < <= == != >= > are supperted in filters.

    >>> from django_dot_filter import V
    >>>
    >>> qs = Product.objects.filter(V.category.name == 'books',
    >>>                             V.name >= 'B', V.name < 'F',
    >>>                             (V.price < 15) | (V.date_created != today),
    >>>                             ~V.option.in_(['ABC', 'XYZ'])
    >>>                             )

    This is the same as

    >>> qs = Product.objects.filter(category__name='books',
    >>>                             name__gte='B', name__lt='F',
    >>>                             Q(price__lt=15) | ~Q(date_created=today),
    >>>                             ~Q(option__in=['ABC', 'XYZ'])
    >>>                             )
    """

(Класс "V" автоматически создает новый экземпляр, если используется с точкой. Все элементы скомпилированы в выражение Q после реляционного оператора или после метода .in_(iterable), и экземпляр снова удаляется.)

Некоторые примеры из тестов

    #       this is V. syntax         compiled Q syntax
    test_eq(V.a.b.c == 1,             Q(a__b__c=1))
    test_eq(V.a == 1,                 Q(a=1))
    test_eq(V.a != 1,                 ~Q(a=1))
    test_eq(V.a < 2,                  Q(a__lt=2))
    test_eq(V.a <= 3,                 Q(a__lte=3))
    test_eq(V.a > 'abc',              Q(a__gt='abc'))
    test_eq(V.a >= 3.14,              Q(a__gte=3.14))
    test_eq((V.a == 1) & (V.b == 2),  Q(a=1) & Q(b=2))
    test_eq((V.a == 1) | (V.b == 2),  Q(a=1) | Q(b=2))
    test_eq((V.a == 1) | ~(V.b == 2), Q(a=1) | ~Q(b=2))
    # method "in_(..)" is used because the word "in" is reserved.
    test_eq(V.first_name.in_([1, 2]), Q(first_name__in=[1, 2]))
    test_eq(~V.a.in_(('Tim', 'Joe')), ~Q(a__in=('Tim', 'Joe')))

    # this should be eventually improved to support all lookup
    # functions automatically e.g. by ".contains('abc')" instead of "=="
    test_eq(V.a.contains == 'abc',    Q(a__contains='abc'))

Это немного шутка, вдохновленная вашим вопросом, но она работает. Я помню старую дискуссию (основные разработчики, смутные воспоминания) о том, что синтаксис name__operator=value больше не будет использоваться, если Django будет новым проектом. Это очень красноречиво, но менее читаемо. Слишком поздно иметь два официальных синтаксиса.

Ответ 5

Я думаю, что ответ зависит от того, как часто происходят такие вещи. Если вы склонны использовать такие ключи повсюду в своем коде, крошечная оболочка, подобная решения, предложенные в ответах, могут быть полезны.

Если это разовый случай (хорошо, а не разовый, но редкий случай), я оставил бы его как есть и просто отметьте его # noqa индикатор, чтобы сделать линтеры и рецензентов кода счастливыми, иначе вы просто значительно затруднили бы удобочитаемость, потому что не очевидно, что вы делаете все эти трюки только для того, чтобы сократить длину ключа

Кстати стиль стиля Google предлагает несколько исключений из правила с 79 столбцами https://google.github.io/styleguide/pyguide.html?showone=Line_length#Line_length (длинные операторы импорта и URL-адреса в комментариях), поэтому любые правила должны соблюдаться мудро