sorununa kısa intoduction ...SQL'de Django özel veritabanı işlev çağrısı etrafında parantez nasıl engellenir?
- PostgreSQL
UNNEST
veANY
gibi onlara çok düzgün dizi alanları (int dizi dize dizi) ve işlevlere sahiptir. - Bu alanlar, Django tarafından desteklenmektedir (bunun için
djorm_pgarray
kullanıyorum), ancak işlevler doğal olarak desteklenmemektedir. - Bir kullanıcı
.extra()
kullanabilir, ancak Django 1.8, database functions'un yeni bir konseptini tanıttı.
Tüm bunlarla temel olarak ne yaptığımın en ilkel örneğini vereyim. Bir Dealer
, desteklediği markaların bir listesini içerir. Bir Vehicle
bir satıcıya bağlıdır ve bir satıcıya bağlıdır. Ancak Vehicle
'un yapısının Dealer
' un yapım listesiyle eşleşmesi kaçınılmazdır.
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)
bayi veritabanına sahip ve yapar, ben aracın marka ve onun Satıcının yapmak liste maçı yapmak kendisi için tüm araçları saymak istiyor. Ben .extra()
kaçınarak böyle yaparım.
from django.db.models import functions
class SelectUnnest(functions.Func):
function = 'SELECT UNNEST'
...
Vehicle.objects.filter(
make__in=SelectUnnest('dealer__make_list')
).count()
Ortaya 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"))
Ve çalıştığını ve biz Django kullanabilirsiniz geleneksel M2M yaklaşımı çok daha hızlı. AMA, bu görev için UNNEST
çok iyi bir çözüm değildir: ANY
çok daha hızlıdır. Hadi deneyelim.
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"))
Ve ANY
etrafında parantez sahte oldukları için, başarısız:
class Any(functions.Func):
function = 'ANY'
...
Vehicle.objects.filter(
make=Any('dealer__make_list')
).count()
Aşağıdaki SQL oluşturur. Onları kaldırırsanız, psql
konsolunda sorunsuz ve hızlı bir şekilde çalışır.
Benim sorum.
- Bu diş tellerini çıkarmanın bir yolu var mı? Django belgelerinde bunun hakkında hiçbir şey bulamadım.
- Değilse, - belki bu sorguyu yeniden ifade etmenin başka yolları var mı?
P. S. Farklı backendleri için veritabanı geniş bir işlev kitaplık veritabanı ağır Django uygulamaları için çok yararlı olacağını düşünüyoruz. Tabii ki bunların çoğu taşınabilir olmayacaktır. Fakat genellikle böyle bir projeyi bir veritabanından arka uçtan diğerine geçiremezsiniz. Örneğimizde, dizi alanlarını ve PostGIS'i kullanarak PostgreSQL'e yapıştık ve hareket etmeyi düşünmüyoruz.
Böyle bir şey geliştiren var mı?
P. biri bu durumda biz yapar için ayrı bir tabloyu kullanarak olmalı ve yerine dize dizinin intarray, diyebilirsiniz, doğru ve yapılacaktır, ancak sorunun doğası değişmez.
GÜNCELLEME.
TextArrayField
djorm_pgarray tanımlanır. Bağlı kaynak dosyada, nasıl çalıştığını görebilirsiniz.- değer metin dizeleri listesi aşağıdadır. Python'da bir liste olarak temsil edilir. Örnek:
['honda', 'mazda', 'anything else']
. İşte
veritabanında bu konuda söylenen budur.
=# select id, make from appname_tablename limit 3;
id | make
---+----------------------
58 | {vw}
76 | {lexus,scion,toyota}
39 | {chevrolet}
Ve altta yatan PostgreSQL alan türü text[]
olduğunu.
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)
ve sorguda:
(bağımlılıkları basitleştirmek için), Func ve bu kadar alt sınıfları sadece can aggreate ve not açıklamalarında kullanılmalı, ancak filtrede kullanılmamalıdır. – e4c5
Hatta, parantezleri çıkarmak için kullanılıp kullanılamayacağını görmek için Func'de as_sql yöntemini geçersiz kılmaya çalıştım. Ancak köşeli parantezlerin – e4c5
@ e4c5 başka bir yerde eklendiğini görüyoruz, ben de kaynağa baktım. Belki de Django ORM internals ile derinden ilgilenen biri var ve buna cevap verebilir. – Altaisoft