21

これまでで最も奇妙なエラーが発生しました。Person モデルがあります

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 フィールドとまったく同じように設計されているため、このエラーは非常に奇妙です。

この理由は何でしょうか?

4

8 に答える 8

44

問題の根本を明確に説明している回答はありません。

通常、データベースではフィールドを作成でき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
(残念ながら、執筆時点では適切な解決策は決定されていません)

于 2015-04-29T22:01:00.373 に答える
23

これは古いものですが、今同様の問題がありましたが、別の解決策を提供します.

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」。

于 2014-02-21T12:26:25.830 に答える
12

null=True, blank=Trueand があるのでunique=True、django はNoneor blank を一意のエントリと見なします。一意性制約を削除し、コードで一意性部分を処理します。

于 2013-06-23T02:12:40.837 に答える
12

これは、フォーム レベルではなくモデル レベルで解決することが重要です。データは API やインポート スクリプト、シェルなどから入力できるためですnull=True。少しあいまいですが、私の経験では一般的に問題ではありません。そのあいまいさを受け入れたい場合は、いくつかの手順でそれを行う方法を次に示します。

null=True, blank=True1)フィールドにセットしてチェンジで移動。

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これで、管理者またはその他のデータ入力方法を使用しているかどうかにかかわらず、空または一意の値として保存できるようになります。

複数の移行を行いたくない場合は、1 回の移行で行う方法の例を次に示します。

# -*- 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),
        ),
    ]
于 2016-07-27T19:01:29.050 に答える
7

問題の根本は、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 が永続化されるようになります。

于 2015-11-04T15:28:59.623 に答える
0

default=Noneフィールドで提供する必要があります

facebook_id = models.CharField(max_length=225,unique=True, null=True,blank=True,default=None)

これは私にとってはうまくいきました。電話番号で使いました。そのため、必須ではなく、入力する場合は一意にすることができます。

于 2022-02-28T07:16:10.443 に答える
-1

空白の値を受け入れており、それらが一意であることを期待しています。これは、twitter_id が空白のエントリは 1 つしか存在できないことを意味します。

あなたはできる

  • 一意の制約を削除するか
  • 空白を削除 =True
  • フィールドのデフォルト値を指定します (ただし、デフォルトは一意である必要があります)
于 2013-06-23T02:17:36.907 に答える