SQLAlchemyの検索条件組み立て


12月からPythonを学び始めました。FlaskとSQLAlchemyを使ってアプリを作ろうとしています。

困ったのが、検索条件の組み立てです。ベタにやるとこんな感じになると思います。名前と苗字を検索条件に入れてユーザーを探すイメージです。

[code]

    def search():
      firstname =''
      lastname = ''
      rs = session.Query(User)
      if firstname:
         rs = rs.filter(User.firstname==firstname)
      if lastname:
         rs = rs.filter(User.lastname==lastname)
    
[/code]

これしか方法が無いなら諦めるのですが、例えば検索条件に入れた変数(この場合はfirstname,lastname)がNoneとか空文字だったらfilterを通しても無視されるような感じに出来たら良いなぁ...と。

ユーザーから与えられる複数のリクエストパラメータに対して、よりスマートに検索条件を組み立てる方法があればご教示頂けると幸いです。




細かい実行確認はしてないですが、多分

[code]

    def search():
      firstname =''
      lastname = ''
      rs = session.Query(User)
      rs = rs.filter((User.firstname==firstname) | (not firstname))
      rs = rs.filter((User.lastname==lastname) | (not lastname))
    
[/code]

で、いけるんじゃないでしょうか（ただ、フィルタにはなってしまいますが）。

`&` `|` は SQLAlchemyがビット演算子をオーバーロードしているものなので、 and, or ではありません。

`&` `|` を使う代わりに `or_`, `and_` というのもあったと思います。

[code]

      rs.filter(or_(User.firstname==firstname, not firstname))
    
[/code]

そうじゃなくて、イコールの比較がそもそもいらないなら （ 検索条件値が SQLでいうところの`firstname = '' or firstname is
null`の時、 DBの値に依らず trueにしたいなら）

[code]

      rs.filter(not firstname).filter(not lastname)
    
[/code]

で、いいと思います。




やはりベタですが、以下の様にも書けるかと思います。

[code]

    rs.filter(and_(and_(User.firstname != '',
                        User.firstname != None,
                        User.firstname == firstname),
                   and_(User.lastname  != '',
                        User.lastname  != None,
                        User.lastname  == lastname))
    
[/code]

`firstname` や `lastname` 変数が空文字列や None であってもマッチするレコードはないはずです。まぁ、DB テーブルの当該カラムに
NOT NULL 制約を設定してしまえば良いのかも知れませんが、空文字列と NULL を別個に扱う DB では不十分かもしれません。

ところで、上記のフィルタがどの様な WHERE 句(SQL)になるのかを確認したい場合には、そのまま print します。

[code]

    print and_(and_(User.firstname != '',
                    User.firstname != None,
                    User.firstname == firstname),
               and_(User.lastname  != '',
                    User.lastname  != None,
                    User.lastname  == lastname))
    =>
    "User".firstname != :firstname_1 AND "User".firstname IS NOT NULL AND
    "User".firstname = :firstname_2 AND "User".lastname != :lastname_1 AND
    "User".lastname IS NOT NULL AND "User".lastname = :lastname_2
    
[/code]

変数などはキーワード変数に置き換えられてしまいますが、確認には十分かと思います。

