PEP8ガイドラインに従って、Python コードをフォーマットし、1 行あたり 80 文字未満に抑えるようにしています。
私たちの SQLAlchemy 行は特に厄介で、チェーンされたメソッドがたくさんあり、複雑なパラメーター、ロジック、ネストされた関数がたくさんあります。
PEP8 の制約を使用して Python SQLAlchemy をフォーマットするための特定のベスト プラクティスはありますか?
私が見つけた最も近い答えはhereですが、私が扱っているコードははるかに複雑です。
より良い解決策を期待してここに来ましたが、括弧の折り返しスタイルが好きだと思います:
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()
)
これは素晴らしく明確で、恐ろしいバックスラッシュを回避します。
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)
バックスラッシュに関する他の意見を歓迎します。
私は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 への繰り返しの再割り当ては、最初は厄介に思えますが、私はそれを乗り越えました。パフォーマンスへの影響は事実上ゼロです。この方法の大きな利点は、末尾のコメントとコメント行の両方を組み合わせてクエリを文書化できることです (上記の無駄な追加を行ったように)。バックスラッシュで行を連鎖すると、ここで制限されます。
このフォーマットの方法は、ロジックによってトリガーされる大量の変更、埋め込まれたスカラー選択などを含む大量のクエリを作成する場合に特にきれいです。
別の例として、SQLAlchemy で生成しているかなり大きな (150 行を超える) CTE クエリがあります。これには、両方の方法を組み合わせた多くの混合ロジック、エイリアシング、およびラベリング (生成されたクエリを読みやすくするために不可欠です) があります。それの大幅に削減された(そしてマングルされた)バージョンは、以下のようなものから始まります:
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 フォーマット スキームのように) 新しい操作で行を先導するようにすれば、非常に読みやすいままです。括弧内の改行も恐れないでください。
ええ、これらは何をしても厄介なものになるので、これらの構造をより短い行に分割できる範囲で、必ずそうしてください。
できない場合は、RHS 全体を括弧で囲むことにより、これらのバックスラッシュをすべて取り除くことができます。Python は、バックスラッシュなしで複数行の構成を適切に解析しますが、それも難しいので、それが良いかどうかを言います。このような場合は、最善の判断を下して、鼻をつまんで飛び込む必要があると思います.