2016-09-06 29 views
3

ポニーORMクエリを実行し、モデルに存在する3つの属性によってクエリをソートしようとします。まず、ソングタイプ別に、ssf_type_order_mapにリストされている5つの値の1つと、duration(int)およびuuid(string)のいずれかになります。曲のタイプについてはポニーORM - 特定の順序で注文

、私は次の順序でソートされた曲を持っていると思います:フルフル(インストゥルメンタル)、ショートパンツ、ループが、私は次のよう.order_by()呼び出しを使用して並べ替えしようとすると、それはdoesnの

茎エラーは返されませんが、前述の順序で必要とするタイプでソートされません(継続時間とUUIDソートはうまく動作します)。

song_source_files = self.song_source_files.select(lambda ssf: True).order_by(lambda ssf: (ssf.type, ssf.duration, ssf.uuid)) 

これは私が理想的なクエリと思われるもので、文字列型を並べ替えることができます。式ssf_type_order_mapがサポートされていないタイプ 『辞書』を持っている」と言って、これを実行するとき

ssf_type_order_map = { 
    'Full': 1, 
    'Full (Instrumental)': 2, 
    'Shorts': 3, 
    'Loops': 4, 
    'Stems': 5 
} 

song_source_files = self.song_source_files.select(lambda ssf: True).order_by(lambda ssf: (ssf_type_order_map[ssf.type], ssf.duration, ssf.uuid)) 

は、しかし、私はエラーを取得します。

ORDER_BY hereのポニーORMのドキュメントは、この文脈でラムダを使用しての非常にあいまいです。

アップデート - 9月7日

私はまた、次のようにモデル上で次のゲッタープロパティを追加しようとしました:

@property 
def source_type(self): 
    ssf_type_order_map = { 
    'Full': 1, 
    'Full (Instrumental)': 2, 
    'Shorts': 3, 
    'Loops': 4, 
    'Stems': 5 
    } 

    return ssy_type_order_map[self.type] 

私は、次のようにクエリを注文してみてください:

song_source_files = self.song_source_files.select(lambda ssf: True).order_by(lambda ssf: (ssf_type_order_map[ssf.type], ssf.duration, ssf.uuid)) 

しかし、私は基本的にモデルは、このプロパティを持っていないというエラーが表示されます。 DjangoのORMと同様の問題に基づく私の前提は、データベースモデルに存在する属性にしかアクセスできないということです。

ポニーもそうだとすれば、私は達成したいことをどうやって引き出すのだろうか?

答えて

3

まず、ポニーは2つのタイプのサブ式、すなわち外部式と相関式を区別します。外部式は、ジェネレータループ変数の値に依存せず、相関式では異なります。我々は二つのサブ表現を持って、このクエリでは

from some_module import f, g 

x = 100 
query = select(e for e in MyEntity if e.attr > f(x, 200) and g(x, e)) 

:次の例を考えてみましょう最初はf(x, 200)で、2番目はg(x, e)です。前者は、ループ変数を使用していないため、外部式としてポニーによって考慮されます。その場合、Ponyはクエリ実行前にPythonで式の値を計算し、その式を単一のパラメータに変換することが可能であると仮定します。そのような式の結果は、Pythonで評価される単一の値に過ぎないため、PonyはPython関数をそれらの内部で使用できる制限を課しません。

2番目の式g(x, e)は、ループ変数eの値に依存するため、Pythonでは評価できません。そのような式の結果は、テーブル行ごとに異なる場合があります。したがって、Ponyはそのような式をSQLに変換する必要があります。すべてのPython式をSQLに変換できるわけではなく、gは、Ponyが翻訳方法を特に知っている関数である必要があります。Ponyは翻訳可能なPython演算のサブセットを定義しています。このサブセットには、数値型、startswithendswithinなどの文字列メソッド、およびsumおよびmaxなどの集計関数の算術演算が含まれます。もし

.order_by(lambda ssf: (ssf_type_order_map[ssf.type], ssf.duration, ssf.uuid)) 

を書き込むときに、コードで

は、式ssf_type_order_map[ssf.type]は、オブジェクト変数ssfを指し、したがって、各テーブルの行のための異なる値を持つことになり、これは、相関式であり、ポニー、その翻訳する必要がありますSQLへの式。現在、ポニーはそのような特定の翻訳をどのように実行するのかを理解していませんが、原則としてこれは実行可能です。翻訳の結果は、次のSQL CASE声明ます:ポニーにはないこの時点で

(1 if ssf.type == 'Full' else 
2 if ssf.type == 'Full (Instrumental)' else 
3 if ssf.type == 'Shorts' else 
4 if ssf.type == 'Loops' else 
5 if ssf.type == 'Stems' else 0) 

ORDER BY CASE ssf.type 
    WHEN 'Full' THEN 1 
    WHEN 'Full (Instrumental)' THEN 2 
    WHEN 'Shorts' THEN 3 
    WHEN 'Loops' THEN 4 
    WHEN 'Stems' THEN 5 
    ELSE 0 
END 

は、良いニュースは、あなたがPythonの場合、表現の構文を使用してポニーでこのような式を書くことができるということですif-expressionの逆コンパイルをサポートしています。そのようなコードを直接書くと、例外が発生します。この問題を回避するには、ラムダ関数のソースを文字列として渡す必要があります。この場合、逆変換せずに文字列を直接ASTに解析できるため、正しく変換されます。だから、書くことができます。

song_source_files = self.song_source_files.select().order_by(""" 
    lambda ssf: ((1 if ssf.type == 'Full' else 
        2 if ssf.type == 'Full (Instrumental)' else 
        3 if ssf.type == 'Shorts' else 
        4 if ssf.type == 'Loops' else 
        5 if ssf.type == 'Stems' else 0), 
       ssf.duration, ssf.uuid) 
""") 

これは完璧に動作するはずですが、私は別の方法でこの問題を解決することをお勧めします:我々はnamecode属性を持つSourceFileType実体を持っているし、その後ssf.type.code値でssfレコードを注文することができます:

class SongSourceFile(db.Entity): 
    name = Required(str) 
    type = Required(lambda: SourceFileType) 
    duration = Required(timedelta) 
    uuid = Required(uuid.UUID, unique=True, default=uuid.uuid4) 

class SourceFileType(db.Entity): 
    name = Required(str) 
    code = Required(int) 
    files = Set(lambda: SongSourceFile) 

それは次のようにクエリを書くことができるようになる:

song_source_files = self.song_source_files.select().order_by(
    lambda ssf: (ssf.type.code, ssf.duration, ssf.uuid) 
) 

namecode以外にも、他の有用な属性をSourceFileTypeに追加できるので、このアプローチはより普遍的だと思います。

+0

Spot on!私はもともとsource_file_typeを表すために追加のモデルを追加することを考えていましたが、保守性の問題については迷っています。長いSQL文字列を持つ最初のソリューションは、完璧に働いた、ありがとう! :) –

関連する問題