У меня есть модель Foo, у которой есть поле. Поле bar должно быть уникальным, но в нём должно быть пустое значение, то есть я хочу разрешить более одной записи, если поле bar есть null
, но если это не так, null
значения должны быть уникальными.
Вот моя модель:
class Foo(models.Model):
name = models.CharField(max_length=40)
bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
А вот соответствующий SQL для таблицы:
CREATE TABLE appl_foo
(
id serial NOT NULL,
"name" character varying(40) NOT NULL,
bar character varying(40),
CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)
При использовании интерфейса администратора для создания более 1 объекта foo, где bar равен null, выдает ошибку: «Foo с этим Bar уже существует».
Однако, когда я вставляю в базу данных (PostgreSQL):
insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)
Это работает, просто отлично, это позволяет мне вставлять более 1 записи с нулевым баром, поэтому база данных позволяет мне делать то, что я хочу, это просто что-то не так с моделью Django. Любые идеи?
РЕДАКТИРОВАТЬ
Переносимость решения для DB не проблема, мы довольны Postgres. Я попытался установить уникальное значение для вызываемого объекта, так как моя функция возвращала True / False для определенных значений bar , он не выдавал никаких ошибок, однако показывал, что он вообще не имел никакого эффекта.
До сих пор я удалил уникальный спецификатор из свойства bar и обработал уникальность bar в приложении, однако все еще искал более элегантное решение. Любые рекомендации?
источник
def get_db_prep_value(self, value, connection, prepared=False)
как вызов метода. Проверьте groups.google.com/d/msg/django-users/Z_AXgg2GCqs/zKEsfu33OZMJ для получения дополнительной информации. У меня тоже работает следующий метод: def get_prep_value (self, value): if value == "": # Если Django пытается сохранить '' строку, отправьте db None (NULL), верните None else: верните значение #otherwise, просто передать значениеОтветы:
Django не считает NULL равным NULL с целью проверки уникальности с момента исправления билета № 9039, см.
http://code.djangoproject.com/ticket/9039
Проблема здесь в том, что нормализованное «пустое» значение для формы CharField представляет собой пустую строку, а не None. Таким образом, если вы оставите поле пустым, вы получите пустую строку, а не NULL, сохраненную в БД. Пустые строки равны пустым строкам для проверок уникальности, как по правилам Django, так и по правилам базы данных.
Вы можете заставить интерфейс администратора хранить NULL для пустой строки, предоставив свою собственную настроенную форму модели для Foo с методом clean_bar, который превращает пустую строку в None:
источник
fields
илиexclude
вModelForm
инстансах. Вы можете обойти это, опускаяMeta
внутренний класс из ModelForm для использования в админке. Справка: docs.djangoproject.com/en/1.10/ref/contrib/admin/...** edit 30.11.2015 : В python 3 глобальная
__metaclass__
переменная модуля больше не поддерживается . Additionaly, по состояниюDjango 1.10
наSubfieldBase
класс был устаревшим :Следовательно, как следует из
from_db_value()
документации и данного примера , это решение должно быть изменено на:Я думаю, что лучшим способом, чем переопределение cleaned_data в админке, было бы создать подкласс charfield - таким образом, независимо от того, какая форма обращается к полю, она будет «просто работать». Вы можете поймать его
''
непосредственно перед отправкой в базу данных и поймать NULL сразу после того, как он выйдет из базы данных, а остальная часть Django не будет знать / заботиться. Быстрый и грязный пример:Для моего проекта я поместил это в
extras.py
файл, который находится в корне моего сайта, а затем я могу простоfrom mysite.extras import CharNullField
вmodels.py
файле моего приложения . Поле действует так же, как CharField - просто не забудьте установить егоblank=True, null=True
при объявлении поля, иначе Django выдаст ошибку проверки (обязательное поле) или создаст столбец db, который не принимает NULL.источник
CharField
чтобы быть aCharNullField
, вам нужно сделать это в три этапа. Сначала добавьтеnull=True
в поле и перенесите это. Затем выполните миграцию данных, чтобы обновить все пустые значения, чтобы они были нулевыми. Наконец, конвертируйте поле в CharNullField. Если вы преобразуете поле перед миграцией данных, миграция данных ничего не изменит.from_db_value()
не должно быть этого дополнительногоcontex
параметра. Должно бытьdef from_db_value(self, value, expression, connection):
Поскольку я новичок в stackoverflow, мне еще не разрешено отвечать на ответы, но я хотел бы отметить, что с философской точки зрения я не могу согласиться с самым популярным ответом на этот вопрос. (Карен Трейси)
ОП требует, чтобы его поле бара было уникальным, если оно имеет значение, и ноль в противном случае. Тогда должно быть, что сама модель убеждается в этом. Он не может быть оставлен на внешний код для проверки этого, потому что это будет означать, что его можно обойти. (Или вы можете забыть проверить это, если напишете новый вид в будущем)
Поэтому, чтобы ваш код был действительно ООП, вы должны использовать внутренний метод вашей модели Foo. Изменение метода save () или поля - хорошие варианты, но использование формы для этого, безусловно, не так.
Лично я предпочитаю использовать предложенный CharNullField для мобильности с моделями, которые я мог бы определить в будущем.
источник
Быстрое решение состоит в том, чтобы сделать:
источник
MyModel.objects.bulk_create()
обойдёт этот метод.Другое возможное решение
источник
Это исправлено теперь, когда https://code.djangoproject.com/ticket/4136 решен. В Django 1.11+ вы можете использовать
models.CharField(unique=True, null=True, blank=True)
без необходимости вручную конвертировать пустые значения вNone
.источник
У меня недавно было такое же требование. Вместо того, чтобы создавать подклассы для различных полей, я решил переопределить метод save () в моей модели (с именем «MyModel» ниже) следующим образом:
источник
Если у вас есть модель MyModel и вы хотите, чтобы my_field было нулевым или уникальным, вы можете переопределить метод сохранения модели:
Таким образом, поле не может быть пустым, будет только непустым или нулевым. нули не противоречат уникальности
источник
Вы можете добавить
UniqueConstraint
с условиемnullable_field=null
и не включать это поле вfields
список. Если вам нужно также ограничение с такимnullable_field
значением, которого нетnull
, вы можете добавить дополнительное.Примечание: UniqueConstraint был добавлен начиная с django 2.2
источник
Что бы там ни было, Django считает,
NULL
что это эквивалентно вNULL
целях проверки уникальности. Там действительно нет никакого способа, кроме как написать собственную реализацию проверки уникальности, которая учитываетNULL
уникальной, независимо от того, сколько раз она встречается в таблице.(и имейте в виду, что некоторые решения БД придерживаются того же взгляда
NULL
, поэтому код, основанный на идеях одной БД,NULL
может не переноситься на другие)источник