ProgrammingError: столбец "продукт" имеет тип product [], но выражение имеет тип text [] enum postgres

Я хотел бы сохранить массив перечислений.

У меня есть следующее:

CREATE TABLE public.campaign
(
  id integer NOT NULL,
  product product[]
)

является enum.

В Django я определил его следующим образом:

PRODUCT = (
    ('car', 'car'),
    ('truck', 'truck')
)
class Campaign(models.Model):
    product = ArrayField(models.CharField(null=True, choices=PRODUCT))

Однако, когда я пишу следующее:

campaign = Campaign(id=5, product=["car", "truck"])
campaign.save()

Я получаю следующую ошибку:

ProgrammingError: column "product" is of type product[] but expression is of type text[]
LINE 1: ..."product" = ARRAY['car...

Примечание Я видел этот ответ, но я не использую sqlalchemy и не буду использовать его, если не нужно.

EDITED Я попробовал следующее предложение @Roman Konoval:

class PRODUCT(Enum):
    CAR = 'car'
    TRUCK = 'truck'

class Campaign(models.Model):
        product = ArrayField(EnumField(PRODUCT, max_length=10))

и с:

campaign = Campaign(id=5, product=[CAR, TRUCK])
campaign.save()

Однако, я все равно получаю ту же ошибку,

Я вижу, что django переводит его в список строк. если я сразу напишу консоль psql:

INSERT INTO campaign ("product") VALUES ('{car,truck}'::product[]) 

он работает просто отлично

Ответ 1

Здесь есть две основные проблемы.

Не использовать Enums

Если вы продолжаете использовать перечисление, ваш следующий вопрос здесь, в Stackoverflow, будет "как добавить новую запись в перечисление?". Django не поддерживает тип перечисления из коробки (слава богу). Поэтому для этого вам необходимо использовать сторонние библиотеки. Ваш пробег будет отличаться от того, насколько полно библиотека.

Значение перечисления занимает четыре байта на диске. Длина перечисления текстовая метка значения ограничена установкой NAMEDATALEN, скомпилированной в PostgreSQL; в стандартном строит это значение не более 63 байтов.

Если вы думаете, что вы сохраняете место на диске с помощью перечисления, приведенная выше цитата из руководства показывает, что это иллюзия.

Смотрите Q & A, чтобы узнать больше о преимуществах и недостатках перечисления. Но в целом недостатки перевешивают преимущества.

Не используйте массивы

Совет. Массивы не являются наборами; поиск определенных элементов массива может быть признак неправильного определения базы данных. Рассмотрите возможность использования отдельной таблицы с строка для каждого элемента, которая будет элементом массива. Это будет проще для поиска и, вероятно, лучше масштабируется для большого количества элементы.

Источник: https://www.postgresql.org/docs/9.6/static/arrays.html

Если вы собираетесь искать кампанию, которая занимается автомобилями или грузовиками, вам придется много работать. Так будет и база данных.

Правильная конструкция

Правильный дизайн - это тот, который предлагается на странице документации по массивам postgresql. Создайте связанную таблицу. Это стандартный способ джанго.

class Campaign(models.Model):
    name = models.CharField(max_length=20)


class Product(Models.model):
    name = models.CharField(max_length=20)
    campaign = models.ForeignKey(Campaign)

Это упрощает ваш код. Не требует дополнительного хранения. Не требует сторонних библиотек. И лучший из всех обширных api django связанных моделей становится доступным для вас.

Ответ 2

Определение поля product неверно, поскольку оно указывает, что это массив из CharField, но это массив перечислений в действительности. Django теперь не поддерживает тип перечисления, поэтому вы можете попробовать это расширение, чтобы правильно определить тип:

class Product(Enum):
  ProductA = 'a'
  ...

class Campaign(models.Model):
  product = ArrayField(EnumField(Product, max_length=<whatever>))

Ответ 3

Попробуйте следующее:

def django2psql(s):
    return '{'+','.join(s) + '}

campaign = Campaign(id=5, product=django2psql(["car", "truck"]))  

Ответ 4

Я думаю, вам может понадобиться подкласс CharField, чтобы сообщить ему правильный db_type. Там может быть больше проблем, чем это, но вы можете попробовать:

class Product(models.CharField):
    def db_type(self, connection):
        return 'product'

PRODUCT = (
    ('car', 'car'),
    ('truck', 'truck')
)

class Campaign(models.Model):
    product = ArrayField(Product(null=True, choices=PRODUCT))