[英]Django CharFIeld with unique=True update error “Instance with this Name already exists”
[英]Django unique, null and blank CharField giving 'already exists' error on Admin page
我遇到了有史以來最奇怪的錯誤。 我有一個人 model
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 with this Twitter id already exists.
我發現這個錯誤非常奇怪,因為 Facebook_id 字段的設計方式與 Twitter_id 字段完全相同。
這可能是什么原因?
沒有一個答案清楚地描述了問題的根源。
通常在數據庫中,您可以創建一個字段null=True, unique=True
並且它會起作用......因為NULL != NULL
。 所以每個空白值仍然被認為是唯一的。
但不幸的是,對於CharField
s Django 將保存一個空字符串""
(因為當您提交表單時,所有內容都以字符串形式進入 Django,您可能真的想保存一個空字符串""
- Django 不知道它是否應該轉換到None
)
這基本上意味着你不應該在 Django 中使用CharField(unique=True, null=True, blank=True)
。 正如其他人所指出的,您可能必須放棄數據庫級唯一約束並在模型中進行自己的唯一檢查。
如需進一步參考,請參見此處: https : //code.djangoproject.com/ticket/4136
(不幸的是,在撰寫本文時沒有確定好的解決方案)
這是一個舊的,但我剛才遇到了類似的問題,盡管我會提供替代解決方案。
我處於一種情況,我需要能夠擁有一個帶有 null=True、blank=True 和 unique=True 的 CharField。 如果我在管理面板中提交空字符串,它將不會提交,因為空字符串不是唯一的。
為了解決這個問題,我覆蓋了 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”。
在 Django 1.11 中,表單CharFields
將有一個empty_value
參數,如果字段為空,它允許您使用None
。
如果模型的CharField
具有null=True
,模型表單,包括 Django 管理員, 將自動設置empty_value=None
。
因此,您將能夠在模型CharField
一起使用null=True
、 blank=True
和unique=True
,而不會導致問題的唯一約束。
在模型級別而不是在表單級別解決這個問題很重要,因為數據可以通過 API、導入腳本、shell 等輸入。在 CharField 上設置null=True
的缺點是該列可能會結束帶有空字符串和 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),
),
]
由於您有null=True, blank=True
和unique=True
,django 將None
或 blank 作為唯一條目。 去掉唯一性約束,處理代碼中的唯一性部分。
問題的根源在於 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 被持久化。
您必須在該字段中提供default=None
facebook_id = models.CharField(max_length=225,unique=True, null=True,blank=True,default=None)
這對我有用。 我在電話號碼中使用了它。 因此,如果輸入並且不是強制性的,它可以是唯一的。
您接受空白值並期望它們是唯一的。 這意味着只能有一個帶有空白 twitter_id 的條目
你可以
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.