Форматирование кода SQLAlchemy
Мы пытаемся следовать рекомендациям PEP8 для форматирования нашего кода на Python и оставаться ниже 80 символов в строке.
Наши строки SQLAlchemy особенно сложны, имеют множество цепочечных методов и множество сложных параметров, логических и вложенных функций.
Существуют ли какие-либо конкретные рекомендации по форматированию SQLAlchemy Python с ограничениями PEP8?
Самый близкий ответ, который я нашел, здесь, но код, с которым я имею дело, намного сложнее.
Ответы
Ответ 1
Пришел сюда, надеясь на лучшее решение, но я думаю, что предпочитаю стиль круглых скобок:
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()
)
Это хорошо и понятно, и избегает ужасной обратной косой черты.
Ответ 2
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)
Я бы приветствовал другие мнения по поводу обратной косой черты.
Ответ 3
Я часто использую обратную косую черту аналогично тому, что указал 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), он остается вполне читаемым. Не бойтесь строк новой строки в скобках.
Ответ 4
Да, это будет неприятно, независимо от того, что вы делаете, поэтому в той мере, в какой вы можете разделить эти конструкции на более короткие строки, определенно сделайте это.
Когда вы не можете, вы, вероятно, можете избавиться от всех этих обратных косых черт, поставив всю RHS в скобки. Затем Python будет разбирать многострочные конструкции без обратных косых черт, но также трудно сказать, лучше это или нет. В таких случаях, я думаю, вам просто нужно использовать свое лучшее суждение, держать нос и погружаться.