Форматирование кода SQLAlchemy - PullRequest
17 голосов
/ 28 февраля 2012

Мы пытаемся следовать правилам PEP8 для форматирования нашего кода Python и сохранения ниже 80 символов в строке.

Наши строки SQLAlchemy особенно проблематичны, так как имеют множество связанных методов и множество сложных параметров, логики и вложенных функций.

Существуют ли конкретные рекомендации по форматированию Python SQLAlchemy с ограничениями PEP8?

Самый близкий ответ, который я нашел, это здесь , но код, с которым я имею дело, намного, намного сложнее.

Ответы [ 4 ]

28 голосов
/ 24 сентября 2013

Пришел сюда в надежде на лучшее решение, но я думаю, что предпочитаю стиль переноса скобок:

subkeyword = (
    Session.query(
        Subkeyword.subkeyword_id, 
        Subkeyword.subkeyword_word
    )
    .filter_by(subkeyword_company_id=self.e_company_id)
    .filter_by(subkeyword_word=subkeyword_word)
    .filter_by(subkeyword_active=True)
    .one()
)

Это красиво и понятно, и избегает страшной обратной косой черты.

8 голосов
/ 28 февраля 2012

pep-8 не поощряет обратную косую черту, но для кода SQLAlchemy я не могу не думать, что они наиболее читабельны, поскольку вы можете сохранять каждую порождающую функцию в начале своей собственной строки. Если в скобках много аргументов, я выделю их и в отдельных строках.

subkeyword = Session.query(
                  Subkeyword.subkeyword_id, 
                  Subkeyword.subkeyword_word
             ).\
               filter_by(subkeyword_company_id=self.e_company_id).\
               filter_by(subkeyword_word=subkeyword_word).\
               filter_by(subkeyword_active=True).\
               one()

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

subq = Session.query(
                Bat.id, 
                func.foo(Bat.x, Bat.y).label('foo')
               ).\
                filter(Bat.id==Bar.name).\
                correlate(Bar).\
                subquery()

subq2 = Session.query(Foo.id, Foo.bar).\
                filter_by(flag>5).\
                subquery()

result = Session.query(
                  subq.c.id,
                  subq.c.foo,
                  subq2.c.bar
                ).\
                join(subq2, 
                     and_(
                      subq.c.id > subq2.c.foo, 
                      subq.bar == subq2.id
                     )
                ).\
                order_by(subq.c.id, subq2.c.bar)

Я бы приветствовал другие мнения по поводу обратной косой черты.

2 голосов
/ 01 мая 2013

Я частый пользователь обратной косой черты, аналогично тому, что указал zzzeek в своем ответе. PEP8 - это просто руководство, не теряйте сон, если нарушаете его!

Однако я также часто использую приведенный ниже тип форматирования, где я украл первый пример zzzeek, ​​слегка подправил его и переформатировал:

q = Session.query(
    Subkeyword.subkeyword_id, 
    Subkeyword.subkeyword_word,
)
q = q.filter_by(subkeyword_company_id=self.e_company_id)  # first filter
q = q.filter_by(subkeyword_word=subkeyword_word)  # 2nd filter
q = q.filter_by(subkeyword_active=True)

if filter_by_foo:
    q = q.filter(Subkeyword.foo == True)

# Run the query (I usually wrap in a try block)...
subkeyword = q.one()

Повторное переназначение q сначала кажется довольно неприятным, но я с этим справился. Влияние на производительность практически равно нулю. Большим преимуществом этого способа является то, что вы можете смешивать как завершающие комментарии, так и строки комментариев для документирования ваших запросов (как я сделал с бесполезными дополнениями выше). Цепочка с обратными слешами ограничивает вас здесь.

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

В качестве другого примера, у меня есть довольно большой (> 150 строк) запрос CTE, который я генерирую в SQLAlchemy, который имеет много смешанной логики, псевдонимов и меток (что важно для читаемости сгенерированного запроса), который смешивается оба метода. Его серьезно уменьшенная (и искаженная) версия начинается примерно так:

cte_init = session.\
    query(
        child1.foo.label("child1_foo"),
        sa.literal(1).label("indent"),  # can comment on non-slashed lines
        child2.bar.label("child2bar"),
        #comments between non-slashed lines ok, too 
        sa.func.MAX(toplevel.baz).label("max_baz"),
    ).\
    select_from(top_level).\
    join(child1,
         child1.id == toplevel.fk_child1_id).\
    join(child2.
         child2.id == toplevel.fk_child2.id).\
    filter(top_level.name == "bogus").\
    cte(name = "cte", recursive = True)

if(use_filter_x):
    cte_init = cte_init.filter_by(x = "whatever")

# etc (no, the above doesn't make any sense)...

В общем, если вы уверены, что начинаете свои строки с новых операций (как это делают многие распространенные схемы форматирования SQL), он остается вполне читабельным. Также не бойтесь перевода строки в скобках.

1 голос
/ 28 февраля 2012

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

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

...