我一直遇到最奇怪的错误。我有个人模型
class Person(models.Model):
user = models.OneToOneField(User, primary_key=True)
facebook_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
twitter_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
suggested_person = models.BooleanField(default=False)
我最近添加了twitter_id字段。当我访问Django管理页面,并尝试将'person'更改为suggested_person时,我收到以下错误:
Person with this Twitter id already exists.
我觉得这个错误非常奇怪,因为Facebook_id字段的设计方式与Twitter_id字段完全相同。
这可能是什么原因?
答案 0 :(得分:16)
这是旧的,但我刚才有类似的问题,虽然我会提供另一种解决方案。
我处于这样一种情况,我需要能够拥有一个CharField,其中null = True,blank = True且unique = True。如果我在管理面板中提交一个空字符串,则不会提交,因为空白字符串不是唯一的。
为了解决这个问题,我重写了ModelForm中的'clean'函数,并在那里检查它是否为空字符串并按顺序返回结果。
class MyModelChangeForm(forms.ModelForm):
class Meta:
model = models.MyModel
fields = ['email', 'name', 'something_unique_or_null',]
def clean_something_unique_or_null(self):
if self.cleaned_data['something_unique_or_null'] == "":
return None
else:
return self.cleaned_data['something_unique_or_null']
这解决了我的问题,而不必牺牲模型字段上的唯一属性。
希望这有帮助。
编辑: 您需要更改我将“something_unique_or_null”放在您的字段名称的位置。例如“clean_twitter_id”。
答案 1 :(得分:14)
没有一个答案清楚地描述了问题的根源。
通常在 db 中,你可以创建一个字段null=True, unique=True
,它可以正常工作......因为NULL != NULL
。因此,每个空白值仍然被认为是唯一的。
但不幸的是,对于CharField
,Django将保存一个空字符串""
(因为当你提交表单时,一切都以字符串形式进入Django,你可能真的想保存一个空字符串{{ 1}} - Django不知道它是否应该转换为""
)
这基本上意味着你不应该在Django中使用None
。正如其他人所指出的那样,您可能不得不放弃数据库级别的唯一约束,并在模型中进行自己独特的检查。
如需进一步参考,请参阅此处:https://code.djangoproject.com/ticket/4136
(遗憾的是,在撰写本文时没有很好的解决方案)
答案 2 :(得分:11)
在Django 1.11中,表单CharFields
将有一个empty_value
参数,如果该字段为空,则允许您使用None
。
模型表单,包括Django管理员,will automatically set empty_value=None
,如果模型CharField
具有null=True
。
因此,您可以在模型null=True
中一起使用blank=True
,unique=True
和CharField
,而不会出现导致问题的唯一约束。
答案 3 :(得分:7)
在模型级别解决此问题非常重要,而不是在表单级别,因为数据可以通过API,导入脚本,shell等进入。设置null=True
的缺点是一个CharField是列可能最后都是空字符串和NULL,这有点模棱两可,但在我的经验中通常不是问题。如果您愿意接受这种模糊性,请按以下步骤操作:
1)在字段上设置null=True, blank=True
并在更改中迁移。
2)按下您的数据,以便将所有现有的空字符串更改为NULL:
items = Foo.objects.all()
for item in items:
if not item.somefield:
item.somefield = None
item.save()
3)向您的模型添加自定义save()
方法:
def save(self, *args, **kwargs):
# Empty strings are not unique, but we can save multiple NULLs
if not self.somefield:
self.somefield = None
super().save(*args, **kwargs) # Python3-style super()
4)在字段上设置unique=True
并将其迁移到该字段中。
现在,无论您是否使用管理员或任何其他数据输入方法,您都可以将somefield
存储为空值或唯一值。
如果您不想进行多次迁移,请参阅单个迁移中的示例:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def set_nulls(apps, schema_editor):
Event = apps.get_model("events", "Event")
events = Event.objects.all()
for e in events:
if not e.wdid:
e.wdid = None
e.save()
class Migration(migrations.Migration):
dependencies = [
('events', '0008_something'),
]
operations = [
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.RunPython(set_nulls),
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True, unique=True),
),
]
答案 4 :(得分:4)
问题的根源是Django将空值保持为空字符串,而不是null。要解决此问题,您可以按如下方式对CharField进行子类化:
class CharNullField(models.CharField):
description = "CharField that stores NULL"
def get_db_prep_value(self, value, connection=None, prepared=False):
value = super(CharNullField, self).get_db_prep_value(value, connection, prepared)
if value=="":
return None
else:
return value
因此get_db_prep_value将确保null被持久化
答案 5 :(得分:0)
由于您有null=True, blank=True
和unique=True
,因此django正在考虑None
或空白作为唯一条目。删除唯一约束并处理代码中的唯一性部分。
答案 6 :(得分:-1)
您接受空白值并期望它们是唯一的。这意味着只能有一个空白的twitter_id
条目你可以