noa*_*ale 13 python django django-models django-admin
我一直在收到最奇怪的错误.我有一个人模型
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)
Run Code Online (Sandbox Code Playgroud)
我最近添加了twitter_id字段.当我访问Django管理页面,并尝试将'person'更改为suggested_person时,我收到以下错误:
Person with this Twitter id already exists.
Run Code Online (Sandbox Code Playgroud)
我发现这个错误非常奇怪,因为Facebook_id字段的设计方式与Twitter_id字段完全相同.
这可能是什么原因?
Ane*_*pic 21
没有一个答案清楚地描述了问题的根源.
通常在数据库中,您可以创建一个字段null=True, unique=True,它将起作用...因为NULL != NULL.因此,每个空白值仍然被认为是唯一的.
但不幸的是,对于CharFields Django将保存一个空字符串""(因为当你提交一个表单时,一切都以字符串形式进入Django,你可能真的想保存一个空字符串""--Django不知道它是否应该转换为None)
这基本上意味着你不应该CharField(unique=True, null=True, blank=True)在Django中使用.正如其他人所指出的那样,您可能不得不放弃数据库级别的唯一约束,并在模型中执行您自己的唯一检查.
如需进一步参考,请参见此处:https://code.djangoproject.com/ticket/4136
(遗憾的是,在撰写本文时尚无最佳解决方案)
Lon*_*Dev 17
这是一个旧的,但我刚才有类似的问题,虽然我会提供一个替代解决方案.
我处于这样一种情况,我需要能够拥有一个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']
Run Code Online (Sandbox Code Playgroud)
这解决了我的问题,而不必牺牲模型字段上的唯一属性.
希望这可以帮助.
编辑:您需要更改我将"something_unique_or_null"放在您的字段名称的位置.例如"clean_twitter_id".
Ala*_*air 11
在Django 1.11中,form CharFields将有一个empty_value参数,None如果该字段为空,则允许您使用该参数.
模型表单,包括Django管理员,将自动设置empty_value=None模型是否CharField具有null=True.
因此,您将能够在模型中一起使用null=True,blank=True而不会出现导致问题的唯一约束.unique=TrueCharField
kar*_*ikr 10
既然你有null=True, blank=Trueand unique=True,django 正在考虑None或空白作为一个独特的条目。去掉唯一性约束,处理代码中的唯一性部分。
在模型级别而不是在表单级别解决此问题非常重要,因为数据可以通过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()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
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),
),
]
Run Code Online (Sandbox Code Playgroud)
问题的根源在于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
Run Code Online (Sandbox Code Playgroud)
因此,get_db_prep_value将确保null持久化