今回のお話はDjangoの
UNIQUE constraint failed...
を解決する。
いやー。長かった。これを解決するため、半日ほど時間を費やした....。
なぜこうなるのか原因は分かっているのにどう解決していいのか分からずただハマっていました。
解決するにもいい方法が調べてもなかなか見つからなかったので同じ悩みを持つ方のため、僕が記事にして残しておきますね。
UNIQUE constraint failed
まず、このエラーの原因は、modelを編集する場合なんかに起こります。既にデータが格納されているmodelをupdate(更新)しようとしたときなんかにハマります。
そして、大体DjanogoのModelをunique=True
で作成している場合にこのエラーに遭遇します。
とりあえずの解決策はunique制約を解除してあげるといいでしょう。
いやいや。unique制約は解除できないよ(したくないよ)って方は他の解決策↓↓でいきましょう。
(僕もこれです。頑なにunique制約は解除したくなかった...)
models.py
こんなmodelを使いたかった。STORE_EMAIL
の部分がunique制約となっていますね。
class StoreInfo(models.Model):
STORE_EMAIL = models.EmailField(max_length=254, unique=True, null=True)
STORE_NAME = models.CharField(max_length=70)
postal_code_regex = RegexValidator(regex=r'^[0-9]+$', message = ("Postal Code must be entered in the format: '1234567'. Up to 7 digits allowed."))
STORE_POSTAL_CODE = models.CharField(validators=[postal_code_regex], max_length=7, verbose_name='郵便番号')
STORE_ADDRESS = models.CharField(max_length=100)
tel_number_regex = RegexValidator(regex=r'^[0-9]+$', message = ("Tel Number must be entered in the format: '09012345678'. Up to 15 digits allowed."))
STORE_TEL = models.CharField(validators=[tel_number_regex], max_length=15, verbose_name='電話番号')
def __str__(self):
return self.STORE_NAME
unique=True
を解除せずに今回のエラーを解決するためには、null=True
を追加してください。
forms.py
formはこのようになっています。特段ここでは修正するポイントもないので、ただ紹介しておきます。
class StoreInfoForm(forms.ModelForm):
class Meta:
model = StoreInfo
fields = ('STORE_EMAIL', 'STORE_NAME', 'STORE_POSTAL_CODE', 'STORE_ADDRESS', 'STORE_TEL')
labels = {
'STORE_EMAIL': 'E_Mail',
'STORE_NAME': '店名',
'STORE_POSTAL_CODE': '郵便番号',
'STORE_ADDRESS': '住所',
'STORE_TEL': '電話番号'
}
help_texts = {
'STORE_EMAIL': 'E_Mailを入力',
'STORE_NAME': '店名を入力',
'STORE_POSTAL_CODE': '郵便番号を入力',
'STORE_ADDRESS': '住所を入力',
'STORE_TEL': '電話番号を入力'
}
views.py
今回のエラー解決は、ここがポイントになります。
ポイント以外はざっと読み飛ばしてもらっても結構です。
def store_mypage_edit(request):
params = {'message': '', 'form': None}
if request.method == 'POST':
form_instance = StoreInfo(STORE_EMAIL=request.user) #現在のユーザー名のインスタンスをmodelから取得
form = StoreInfoForm(request.POST, instance=form_instance)
if form.is_valid():
StoreInfo.objects.filter(STORE_EMAIL=request.user).update(STORE_EMAIL=None) #Emailがユニークキーなので厄介。一旦NULLに書き換えた後にformの内容で更新
form.save()
return redirect('myapp:store_mypage')
else:
params['message'] = '再入力して下さい'
params['form'] = form
else:
params['form'] = StoreInfoForm()
return render(request, 'myapp/debug_mypage_edit.html', params)
結論から言うと、これ↓がポイントになってきます。
StoreInfo.objects.filter(STORE_EMAIL=request.user).update(STORE_EMAIL=None)
要は、modelの更新の前に一旦データをnull
にしています。
そしてその後にformから入力されたデータにform.save()
でupdate(更新)していきます。
「UNIQUE constraint failed」のエラーは既に存在するデータに対して同じデータが書き込まれようとした場合に起こるので、同じデータを書き込む前にnullを経由することで重複データ入力エラー(unique制約)にならないというわけです。
- abc@abc.jp→abc@abc.jp
同一データが入力されたと判断され、エラーとなる。 - abc@abc.jp→null→abc@abc.jp
一旦nullに書き換わっているので再度同一データを入力してもエラーとならない。
ちなみに、form.save()
はmodelデータの新規作成/更新どちらでも行なえます。
では、新規作成と更新をどのように区別しているかと言うと、
新規作成
form = StoreInfoForm(request.POST)
更新
form_instance = StoreInfo(STORE_EMAIL=request.user)
form = StoreInfoForm(request.POST, instance=form_instance)
操作を行うmodelデータのインスタンスを引数でStoreInfoForm
に渡すか渡さないかの違いです。
これを渡すか渡さないかでDjango内で新規作成or更新を判別しています。
UNIQUE constraint failedの解決方法
ざっと、UNIQUE constraint failedの解決方法は
- modelのフィールドを
null=True
としnullを許容する StoreInfo.objects...update(xxx=None)
でnullを経由し、目的のデータへ更新する
です。
同じ悩みの方はぜひ、こちらを試して見てください。
ばいちゃ。
Djangoを始めたい方へおすすめ記事
Djangoのlogin認証に関する記事
Djangoについて学びたい方へ
書籍で学びたいなら
DjangoのWebアプリの設計・作成はもちろん、Webサーバー・アプリ周りの知識も幅広く扱ってくれています。データベースの知識、クラウドの知識、セキュリティの知識も同時に習得でき、初心者にはぜひおすすめの書籍
Djangoについて深く知りたいという方におすすめしています。
ややプログラミングの知識がないと理解が難しいかもしれません。
pythonが書ける方には非常にタメになるDjangoアプリ開発におすすめ書籍でした。
オンライン授業で学びたいなら
僕はオンラインスクールはUdemy をおすすめします。定番の授業からマニアックな授業まで幅広く扱っているので、学びたいことは大体あります。今回はPython+Djangoの授業をおすすめします。Djangoの基礎から応用までこの授業で学べます。
実際にWEBアプリを作成し、公開するところまで親切に教えてくれます。
【徹底的に解説!】Djangoの基礎をマスターして、3つのアプリを作ろう!
コメント