Как избежать скобок в SQL вокруг вызова функции пользовательской базы данных Django?

Короткий ввод в проблему …

  • PostgreSQL имеет очень аккуратные поля массива (int array, string array) и функции для них, такие как UNNEST и ANY .
  • Эти поля поддерживаются Django (для этого я использую djorm_pgarray ), но функции не поддерживаются.
  • Можно использовать .extra() , но Django 1.8 представил новую концепцию функций базы данных .

Позвольте мне представить самый примитивный пример того, что я в основном делаю со всеми этими. У Dealer есть список марок, которые он поддерживает. Vehicle имеет марку и связан с дилером. Но бывает так, что Vehicle не соответствует списку изготовителя, что неизбежно.

 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")) 

И это работает, и намного быстрее, чем традиционный подход M2M, который мы могли бы использовать в Django. НО, для этой задачи UNNEST – не очень хорошее решение: ANY намного быстрее. Давай попробуем.

 class Any(functions.Func): function = 'ANY' ... Vehicle.objects.filter( make=Any('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" = (ANY("myapp_dealer"."make_list")) 

И это терпит неудачу, потому что фигурные скобки вокруг ANY – фиктивные. Если вы их удалите, он запускается в консоли psql без проблем и быстро.

Так что мой вопрос.

  1. Есть ли способ удалить эти фигурные скобки? Я не мог найти ничего об этом в документации Django.
  2. Если нет, – возможно, есть другие способы перефразировать этот запрос?

PS Я думаю, что обширная библиотека функций базы данных для разных бэкэндов будет очень полезна для приложений Django с тяжелой базой данных.

Конечно, большинство из них не будут переносимыми. Но вы обычно не часто переносите такой проект из одного бэкенда базы данных в другой. В нашем примере, используя поля массива и PostGIS, мы привязаны к PostgreSQL и не намерены перемещаться.

Кто-нибудь развивает такую ​​вещь?

PPS Можно сказать, что в этом случае мы должны использовать отдельную таблицу для make и intarray вместо строкового массива, это правильно и будет сделано, но характер проблемы не изменится.

ОБНОВИТЬ.

  • 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[] .

One Solution collect form web for “Как избежать скобок в SQL вокруг вызова функции пользовательской базы данных Django?”

Мне удалось получить (более или менее) то, что вам нужно, используя следующие:

 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) 

и запрос:

 Vehicle.objects.filter(make__any=F('dealer__make_list')).count() 

в результате:

 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")) 

Кстати. вместо этого djorm_pgarray и TextArrayField вы можете использовать собственное django:

 make_list = ArrayField(models.CharField(max_length=200), blank=True) 

(чтобы упростить ваши зависимости)

  • Вставить dataframe в sqlalchemy postgresql с автоинкрементами idx
  • Как Django UUIDField генерирует UUID в Postgresql?
  • psycopg2 cursor.execute () с параметром SQL query вызывает синтаксическую ошибку
  • Django JSONField внутри ArrayField
  • Есть ли ограничение на количество таблиц, которые может иметь база данных PostgreSQL?
  • Python (GTK): Может кто-нибудь объяснить разницу между TreeStore, Listmodel и всем остальным?
  • SQLAlchemy или psycopg2?
  • Django, Postgres - колонка не может быть автоматически добавлена ​​для ввода целого числа
  • Python - лучший язык программирования в мире.