問題への短いintoduction ...Djangoのカスタムデータベース関数呼び出しの回りにSQLの括弧を避ける方法は?
- PostgreSQLは
UNNEST
とANY
のような彼らのために非常にきちんと配列フィールド(int配列、文字列配列)と機能を持っています。 - これらのフィールドはDjangoでサポートされていますが(私は
djorm_pgarray
を使用しています)、関数はネイティブにサポートされていません。 .extra()
を使うことができましたが、Django 1.8はdatabase functionsの新しいコンセプトを導入しました。
私が基本的にこれらのすべてでやっていることの最も基本的な例を提供しましょう。 Dealer
には、それがサポートするmakeのリストがあります。 Vehicle
にはメイクがあり、ディーラーにリンクされています。しかし、Vehicle
のmakeがDealer
のmakeリストと一致しないことは避けられません。
MAKE_CHOICES = [('honda', 'Honda'), ...]
class Dealer(models.Model):
make_list = TextArrayField(choices=MAKE_CHOICES)
class Vehicle(models.Model):
dealer = models.ForeignKey(Dealer, null=True, blank=True)
make = models.CharField(max_length=255, choices=MAKE_CHOICES, blank=True)
ディーラーのデータベースを持ち、車両メイクとそのディーラーのメイクリストが一致するすべての車両を数えたいと思います。それは私が.extra()
を避ける方法です。
from django.db.models import functions
class SelectUnnest(functions.Func):
function = 'SELECT UNNEST'
...
Vehicle.objects.filter(
make__in=SelectUnnest('dealer__make_list')
).count()
結果のSQL:
SELECT COUNT(*) AS "__count" FROM "myapp_vehicle"
INNER JOIN "myapp_dealer"
ON ("myapp_vehicle"."dealer_id" = "myapp_dealer"."id")
WHERE "myapp_vehicle"."make"
IN (SELECT UNNEST("myapp_dealer"."make_list"))
そして、それが動作する、と私たちはDjangoのに使用することができ、従来のM2Mのアプローチよりもはるかに高速。しかし、この作業のためにUNNEST
は非常に良い解決策ではありません:ANY
ははるかに高速です。試してみよう。
class Any(functions.Func):
function = 'ANY'
...
Vehicle.objects.filter(
make=Any('dealer__make_list')
).count()
これは、次のSQLを生成:ANY
周りにカッコが偽であるため、
SELECT COUNT(*) AS "__count" FROM "myapp_vehicle"
INNER JOIN "myapp_dealer"
ON ("myapp_vehicle"."dealer_id" = "myapp_dealer"."id")
WHERE "myapp_vehicle"."make" =
(ANY("myapp_dealer"."make_list"))
をそして、それは失敗します。それらを削除すると、問題なく速くpsql
コンソールで実行されます。
私の質問です。
- これらのブレースを削除する方法はありますか?私はDjangoのドキュメントでそれについて何も見つかりませんでした。
- そうでない場合は、このクエリを別の言い方で書き換えることができますか?
P. S.私は別のバックエンドのデータベース機能の豊富なライブラリがデータベース・重いDjangoのアプリのために非常に参考になると思います。
もちろん、これらのほとんどはポータブルではありません。しかし、通常、そのようなプロジェクトをあるデータベースバックエンドから別のデータベースバックエンドに移行することはしばしばありません。この例では、配列フィールドとPostGISを使用してPostgreSQLに固執しており、移動するつもりはありません。
誰もこのようなことを開発していますか?
P. P.この場合、文字列配列の代わりにmakeとintarrayのために別のテーブルを使用する必要がありますが、それは正しく行われますが、問題の性質は変わりません。
UPDATE。
TextArrayField
djorm_pgarrayで定義されています。リンクされたソースファイルで、どのように動作するかを見ることができます。- 値はテキスト文字列のリストです。 Pythonでは、それはリストとして表されます。例:
['honda', 'mazda', 'anything else']
。
これはデータベース内でこれについて述べられています。
=# select id, make from appname_tablename limit 3;
id | make
---+----------------------
58 | {vw}
76 | {lexus,scion,toyota}
39 | {chevrolet}
基礎となるPostgreSQLのフィールドタイプはtext[]
です。ところで
SELECT COUNT(*) AS "__count" FROM "zz_vehicle"
INNER JOIN "zz_dealer" ON ("zz_vehicle"."dealer_id" = "zz_dealer"."id")
WHERE "zz_vehicle"."make" = ANY(("zz_dealer"."make_list"))
:結果として
Vehicle.objects.filter(make__any=F('dealer__make_list')).count()
:
from django.db.models.lookups import BuiltinLookup
from django.db.models.fields import Field
class Any(BuiltinLookup):
lookup_name = 'any'
def get_rhs_op(self, connection, rhs):
return " = ANY(%s)" % (rhs,)
Field.register_lookup(Any)
とクエリ:
これが最も興味深いのですが、それが明示的にドキュメントに記載されていないが、私は、のFuncを考えると、それはサブクラスができるだけですaggreateとannotateでは使用できますが、フィルタでは使用できません。 – e4c5
私はFuncでas_sqlメソッドをオーバーライドして括弧を取り除くことができるかどうかを調べることさえ試みました。しかし、大括弧が他の場所に追加されていることがわかりました。 – e4c5
@ e4c5はい、私もソースを調べました。たぶん、Django ORMの内部に深く関わっている人がいて、それに答えることができます。 – Altaisoft