[解決済み] SQLAlchemyにできて、Django ORMにできないことの例
質問
私は最近、SQLAlchemyでPyramidを使うか、Djangoで現在のアプリケーションを維持するかについて、多くの研究をしています。 それ自体、全体の議論になりますが、私はそれを議論するためにここにいるわけではありません。
私が知りたいのは、なぜ SQLAlchemy が Django ORM よりも良いと普遍的に考えられているのか、ということです。 私が見つけた2つの比較では、すべてではないにしても、ほとんどすべてが SQLAlchemy を支持しています。 SQLAlchemyの構造によって、SQLへの変換がよりスムーズに行えるので、パフォーマンスが大きな要因だと思います。
しかし、より困難なタスクでは、Django ORM はほとんど使えないという話も聞きます。 これがどれほど大きな問題なのか、調査してみたいのです。 SQLAlchemyに乗り換える理由の1つは、Django ORMがニーズに合わなくなったときだと読んだことがあります。
要するに、SQLAlchemy はできるが、Django ORM は生の SQL を追加しないとできないようなクエリ (実際の SQL 構文である必要はない) を誰か教えてください。
更新 :
私が最初に質問してから、この質問がかなり注目されていることに気づいたので、私の追加意見を述べたいと思います。
最終的に私たちはSQLAlchemyを使うことになりましたが、その決断に満足していると言わざるを得ません。
私は、今のところ Django ORM で再現できていない SQLAlchemy の追加機能を提供するために、この質問を再確認しています。 もし誰かがこれを行う方法の例を提供してくれるなら、私は喜んで私の言葉を食べます。
例えば、ファジーな比較を行う similarity() のような postgresql の関数を使いたいとしましょう ( 参照。 PostgreSQLで類似した文字列を素早く見つける - tl;dr input two strings get back a percent similarity) のような関数を使いたいとします。
Django ORM を使ってこれを行う方法についていくつか検索してみましたが、彼らのドキュメントから明らかなように、生の SQL を使うこと以外には何も見つかりませんでした。 https://docs.djangoproject.com/en/dev/topics/db/sql/ .
すなわち
Model.objects.raw('SELECT * FROM app_model ORDER BY \
similarity(name, %s) DESC;', [input_name])
しかし、SQLalchemy はここで説明されているように、func() を持っています。 http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.func
from sqlalchemy import desc, func
session.query(Model).order_by(func.similarity(Model.name, input_name))
これにより、定義されたsql/postgresql/etcの関数に対してsqlを生成し、生のsqlを必要としないようにすることができます。
どのように解決するのですか?
これは非建設的であることに危険なほど近いのですが、私は噛んでみます。
多くの異なる、例えば口座のための特定のアイテムのインベントリを維持する必要があるとします。DDLは次のとおりです。
CREATE TABLE account (
id serial PRIMARY KEY,
...
);
CREATE TABLE item (
id serial PRIMARY KEY,
name text NOT NULL,
...
);
CREATE TABLE inventory (
account_id integer NOT NULL REFERENCES account(id),
item_id integer NOT NULL REFERENCES item(id),
amount integer NOT NULL DEFAULT 0 CHECK (amount >= 0),
PRIMARY KEY (account_id, item_id)
);
まず第一に、 Django ORM は複合主鍵を扱うことができません。確かに、サロゲートキーとユニーク制約を追加することはいつでもできますが、それは実際に必要な列とインデックスを1つ増やすことになります。カラム数が少ない大きなテーブルの場合、これはサイズとパフォーマンスのオーバーヘッドを著しく増加させることになります。また、ORM は一般に、主キー以外のものを使用する ID マッピングに問題を抱えています。
さて、与えられたアカウントのインベントリ内の各アイテムの数量を問い合わせたいが、数量が0に設定されたそこに存在しないすべてのアイテムも含めたいとします。そして、これを数量で降順にソートします。対応するSQLです。
SELECT item.id, item.name, ..., coalesce(inventory.amount, 0) AS amount
FROM item LEFT OUTER JOIN inventory
ON item.id = inventory.item_id AND inventory.team_id = ?
ORDER BY amount DESC;
Django ORM には、カスタム条件による外部結合を表現する方法はありません。しかし、2つの単純なクエリを作成し、 Python ループの中で手作業で結合を実行することは可能です。そして、おそらくパフォーマンスはそれほど悪くならないでしょう
この特別な場合
. しかし、それは重要ではありません。
それぞれ
クエリの結果をアプリケーション側で再現するには、基本的な
SELECT
s.
SQLAlchemyで。
class Account(Base):
__tablename__ = 'account'
id = Column(Integer, primary_key=True)
...
class Item(Base):
__tablename__ = 'item'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
...
class Inventory(Base):
__tablename__ = 'inventory'
account_id = Column(Integer, ForeignKey('account.id'), primary_key=True,
nullable=False)
account = relationship(Account)
item_id = Column(Integer, ForeignKey('item.id'), primary_key=True,
nullable=False)
item = relationship(Item)
amount = Column(Integer, CheckConstraint('amount >= 0'), nullable=False,
default=0)
account = session.query(Account).get(some_id)
result = (session
.query(Item, func.coalesce(Inventory.amount, 0).label('amount'))
.outerjoin(Inventory,
and_(Item.id==Inventory.item_id, Inventory.account==account))
.order_by(desc('amount'))
.all())
余談ですが、SQLAlchemy は辞書ベースのコレクションを非常に簡単に作ることができ ます。以下のコードを
Account
との関係を作ることができます。
Inventory
の関係は、アイテムからその数量へのマッピングとして表示されます。
items = relationship('Inventory',
collection_class=attribute_mapped_collection('item_id'))
inventory = association_proxy('items', 'amount',
creator=lambda k, v: Inventory(item_id=k, amount=v))
これにより、次のようなコードを書くことができます。
account.inventory[item_id] += added_value
のエントリを透過的に挿入または更新するものです。
inventory
テーブルのエントリを挿入または更新します。
複雑な結合、サブクエリ、ウィンドウ集約 - Django ORM は、生の SQL にフォールバックしないと、そのようなものを扱うことができません。
関連
-
[解決済み] Pythonのリストメソッドであるappendとextendの違いは何ですか?
-
[解決済み] 割り当て後にリストが予期せず変更されました。その理由と防止策を教えてください。
-
[解決済み] パラメータに**(ダブルスター/アスタリスク)、*(スター/アスタリスク)がありますが、これはどういう意味ですか?
-
[解決済み] どうすれば、文字列中のリテラルな中抜き文字を印刷し、また.formatを使用することができるのでしょうか?
-
[解決済み] オブジェクト名の前のシングルアンダーコアとダブルアンダーコアの意味は何ですか?
-
[解決済み] SQLAlchemy: flush() と commit() の違いは何ですか?
-
[解決済み】__str__と__repr__の違いは何ですか?
-
[解決済み】type()とisinstance()の違いは何ですか?)
-
[解決済み】Djangoでnull=Trueとblank=Trueの違いは何ですか?
-
[解決済み】Django ORMのselect_relatedとprefetch_relatedの違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] PostgreSQLで類似した文字列を素早く検索する
-
[解決済み] 2つの線分が交差しているかどうかを確認するにはどうすればよいですか?
-
[解決済み] pandasのDataFrameから空のセルを含む行を削除する
-
[解決済み] SQLAlchemy: 日付フィールドをフィルタリングする方法は?
-
[解決済み] なぜ(0-6)は-6=偽なのか?重複
-
[解決済み] Django Rest Framework ファイルアップロード
-
[解決済み] python-requests モジュールからのすべてのリクエストをログに記録します。
-
[解決済み] あるオブジェクトが数であるかどうかを確認する、最もパイソン的な方法は何でしょうか?
-
[解決済み] Cythonのコードを含むPythonパッケージはどのように構成すればよいのでしょうか?
-
[解決済み] virtualenvsはどこに作成するのですか?