Django の勉強をしていて下記のエラーが出て困ったので、備忘録の意味も含めてこのエラーの原因と対処法について解説しておきます。
ValueError: The Xxxx could not be created because the data didn't validate.
Xxxx
の部分には models.py
で定義したクラス名が入ります。
Contents
エラーの原因と対処法
最初に結論として、上記エラーの原因と対処法について解説します。
エラーの原因
このエラーが出る原因は単純明快で、あなたがデータベースに反映しようとしてフォームに入力した値が「妥当でない」ことが原因です。
おそらく、初めて ModelForm
を使って試しにデータベースへのレコードの保存を行おうとしたときに発生しやすいエラーになると思います。
例えば、メールアドレスの入力フォームでお試しで a@b.c
等のてきとうなものを入力して送信すると、このエラーが発生します。これは、入力した文字列がメールアドレスとして妥当でないからです(そんなメールアドレスあり得ないよね…と判断された)。
また、日付の入力フォームでも日付の指定形式が間違っていたりすると、このエラーが発生します。
ちなみに、エラーメッセージの最後にある validate
とは、コンピュータ用語としては「妥当性を検証する」と言う意味で使用されることの多い単語です。
エラーメッセージで validate
・validation
・バリデーション
などの用語が出てきた場合は、エラーの原因が「データが妥当でない」である場合が多いです。例えば、メールアドレスを表すデータを期待しているのにメールアドレスとして妥当でない(@
がない、@
の後ろ側に .
がない等)、みたいな感じです。
今回の場合は「フォームに入力されるデータとして期待しているもの」に対し、実際に入力されたデータが妥当でないために発生するエラーとなります。
スポンサーリンク
エラーの対処法
あなたが Django の学習中であったり、お試しでレコードのデータベースへの保存を行なっているだけであれば、とりあえずフォームに入力する値を妥当にすることで対処できるはずです。
例えばメールアドレスの入力フォームであれば、a@example.com
などのように、ドメイン(@
よりも後ろの部分)に妥当そうなものを指定すれば良いです。メールアドレスの場合、おそらくトップドメイン(最後の .
よりも後ろの部分)に com
や jp
などの実在するものを指定するのが一番無難です。
そもそもエラーが出ないようにしっかり対処したい場合は、データベースにレコードを保存する前に is_valid
メソッド等を利用して入力されたデータ(値や文字列)の妥当性を検証し、問題ない場合のみデータベースへの保存を行うように制御する必要があります。
ただ、もしあなたが Django 学習中なのであれば、おそらく必要なタイミングでバリデーションや妥当性の検証について、さらにはそれらを行う方法について学ぶ機会があるはずです。
なので、前述のように、とりあえず妥当な値をフォームに入力してエラーを回避しておき、学習を進めて行けば良いと思います。
エラーが発生する際の Django の動作
ここからは、どういう動作によって下記のエラーが発生するのかについて解説していきます。上記の内容でエラーの回避法は分かると思いますので、ここからは読みたい方だけ読み進めてください。
ValueError: The Xxxx could not be created because the data didn't validate.
妥当でない値があるのに ModelForm
クラスの save
メソッドを実行するとエラーになる
このエラーは、下記のように models.py
と forms.py
と views.py
を作成した場合に発生する可能性があります(その他の html
や css
等については省略します)。
from django.db import models
class Test(models.Model):
name = models.CharField(max_length=100)
mail = models.EmailField(max_length=200)
def __str__(self):
return '<test:id=' + str(self.id) + ', ' + self.name + '(' + str(self.mail) + ')>'
from django import forms
from.models import Test
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ['name','mail']
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import redirect
from .models import Test
from .forms import TestForm
def index(request):
data = Test.objects.all()
params = {
'title': 'Hello',
'data': data,
}
return render(request, 'hello/index.html', params)
def create(request):
if (request.method == 'POST'):
obj = Test()
test = TestForm(request.POST, instance=obj)
test.save()
return redirect(to='/hello')
params = {
'title': 'Hello',
'form': TestForm(),
}
return render(request, 'hello/create.html', params)
おそらく、Django やデータベースに詳しい人なら、すぐにエラーが発生する原因や、このソースコードが良くない理由は直ぐに分かるのではないかと思います。
このように各スクリプトを作成してページにアクセスすれば、下の図のような入力フォームが表示されます。
入力後に画面下側のボタンを押せば、views.py
の create
関数が実行され(request.method
は 'POST'
)、入力内容に応じたレコードが作成されてデータベースに保存されることになります(そうなるように html
ファイルや urls.py
を作成しています)。
ウェブブラウザによる妥当性の検証
ここで下側の入力フォームに注目すれば、このフォームは models.py
の下記に関連づけられて作成されたものということになります。
mail = models.EmailField(max_length=200)
で、上記でフィールドとして EmailField
を指定しているため、このフォームに「”メールアドレスの形式でない” 文字列」が入力された場合、フォームの値が送信されないよう、ウェブブラウザで妥当性の検証が行われるようになっています。
例えば @
が含まれない文字列を入力してボタンを押そうとしても、フォームからメッセージが表示されボタンが押せません。これは、フォームに入力された文字列に対してウェブブラウザによって妥当性の検証が行われているからです。妥当でない入力はそもそも送信できないようになっています。
なので、例えば a@b.c
などのように、@
を含むメールアドレスとして妥当な文字列変更してやる必要があります。そうすればウェブブラウザが入力された値が妥当であると判断し、ボタンを押した際にフォームに入力した情報をサーバーに送信することができるようになります。
データベース保存前の妥当性の検証
ただ、この例のように、a@b.c
などのような「てきとうなメールアドレスを入力して送信すること」こそが、下記のエラーが発生する原因となります。
The Test could not be created because the data didn't validate.
実は、妥当性の検証はウェブブラウザだけでなく、Django 側でも行われます。そして、妥当かどうかの判断がウェブブラウザと Django では異なるようです。
この妥当性の検証が行われるのが、今回の views.py
の場合、下記の save
メソッド実行時になります。
test = TestForm(request.POST, instance=obj)
test.save()
TestForm
クラスは forms.py
で定義した ModelForm
クラスのサブクラスです。
この ModelForm
の save
メソッドはデータベースへのレコードの保存を行うメソッドですが、その保存を行う前にレコードの各値の妥当性の検証が行われます。
この時の妥当性の検証の仕方はウェブブラウザとは異なります。ですので、ウェブブラウザが “妥当である” と判断した場合でも、save
メソッド内で行われる妥当性の検証で “妥当でない” と判断される場合があります。
そして、妥当であると判断された場合はレコードがデータベースに保存されますが、妥当でないと判断された場合に例外が発生します。
その例外が発生した際に出力されるエラーの文言が、ページの題名になっているエラーになります。
具体的には、save
メソッド実行直後に下記の処理が動作し、if self.errors
が成立して例外が発生してエラーになるようになっているようです(django/forms/models.py
の save
メソッド)
def save(self, commit=True):
"""
略
"""
if self.errors:
raise ValueError(
"The %s could not be %s because the data didn't validate." % (
self.instance._meta.object_name,
'created' if self.instance._state.adding else 'changed',
)
)
つまり、このエラーは、フォームから送信された値が妥当でないのに、ModelForm
の save
メソッドを利用してデータベースに保存しようとした場合に発生するということになります。
ですので、フォームに入力する値を妥当なものに変更することで、このエラーは解決できると思います。
ちょっとどういう仕組みで妥当性の検証が行われるかまでは理解していないですが、例えばメールアドレスであれば a@b.c
などを送信するとエラーになりますが、a@b.jp
などであればエラーになりませんので、最後の .
の後ろ側にはそれっぽいものを選ばないとダメな気がしますね。
また、メールアドレスだけでなく、日付なども適した形式でないと妥当でないと判断されるので注意が必要です。
妥当性の検証を行なってから save
メソッドを実行すれば解決
ただ、ウェブブラウザで妥当性の検証が行われるといっても、ユーザーが妥当でない値をフォームに入力して送信されることは普通にあり得る話です。
その度にページの題名のようなエラーを表示するのはイマイチです。なので、save
メソッドの中ではなく、事前に妥当性の検証を行い、妥当である場合のみ save
メソッドを実行するようにする必要があります。
このフォームの入力値の妥当性の検証は、例えば ModelForm
クラスの is_valid
メソッドにより行うことができます。is_valid
メソッドが True
を返却した場合、フォームの入力値は妥当であると判断できますし、False
を返却した場合は妥当でないと判断することができます。
ですので、False
を返却した場合は save
メソッドを実行しなければ、本ページの題名のエラーの発生を防ぐことができます。
イメージとしては、save
メソッド実行箇所を下記のように変更すれば良いです。
def create(request):
if (request.method == 'POST'):
obj = Test()
test = TestForm(request.POST, instance=obj)
if test.is_valid():
test.save()
return redirect(to='/hello')
else:
# フォーム入力値が妥当でない場合の処理
# test.save() は実行しない
params = {
'title': 'Hello',
'form': TestForm(),
}
return render(request, 'hello/create.html', params)
例えば is_valid
が False
の際の処理を下記のようにすれば、妥当でない値が送信された際に同じページを表示して再度入力を受け付けるようなこともできます。is_valid
が False
の場合は save
メソッドを実行していないのでエラーが発生しません。
def create(request):
if (request.method == 'POST'):
obj = Test()
test = TestForm(request.POST, instance=obj)
if test.is_valid():
test.save()
return redirect(to='/hello')
else:
params = {
'title': 'Hello',
'form': test,
}
return render(request, 'hello/create.html', params)
params = {
'title': 'Hello',
'form': TestForm(),
}
return render(request, 'hello/create.html', params)
上記は1つの対処策の例です。詳しくは Django のバリデーションの解説を読んでみると良いと思います。
スポンサーリンク
Model
クラスの save
メソッドでは妥当性の検証が行われない
なぜ、このエラーで私がハマったかというと、それは、同じフォームの入力値であったのにも関わらず、ModelForm
を利用するようにしただけでエラーが発生するようになってしまったからです。
それまでは通常通りデータベースに保存できていた入力値が、ModelForm
を利用するだけでエラーが出るようになってしまう理由が分かりませんでした。
ちなみに、ModelForm
を利用する前は下記のように forms.py
と views.py
を書いていました(models.py
は前述の例と同じ)。この場合だと、メールアドレスのフォームに a@b.c
などのてきとうな文字列を入力してもエラーが発生しません。
from django import forms
class TestForm(forms.Form):
name = forms.CharField(label='Name', \
widget=forms.TextInput(attrs={'class':'form-control'}))
mail = forms.EmailField(label='Email', \
widget=forms.EmailInput(attrs={'class':'form-control'}))
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import redirect
from .models import Test
from .forms import TestForm
def index(request):
data = Test.objects.all()
params = {
'title': 'Hello',
'data': data,
}
return render(request, 'hello/index.html', params)
def create(request):
params = {
'title': 'Hello',
'form': TestForm(),
}
if (request.method == 'POST'):
name = request.POST['name']
mail = request.POST['mail']
test = Test(name=name,mail=mail)
test.save()
return redirect(to='/hello')
return render(request, 'hello/create.html', params)
この場合は、views.py
では ModelForm
のサブクラスである TestForm
は利用せず、models.py
で定義した Model
のサブクラスの Test
のオブジェクトが save
メソッドを実行しています。
Django の動きをいろいろ確認してみたのですが、どうも Model
クラスの save
メソッドでは、save
メソッドの中で妥当性の検証は行われないようです。
ですので、ModelForm
クラスの save
メソッドだと妥当でないと判断されてエラーになるような場合でも、Model
クラスの save
メソッドだとそもそも妥当性の検証が行われないのでエラーにならず、データベースにレコードの保存ができてしまいます。
そして、この違いがあるので、今までエラーが発生しなかったフォームの入力値が突然エラーになるようになってしまったようです。私と一緒で、ここで戸惑う人もいるんじゃないかなぁと考え、このページを作成することにしました。
まとめ
このページでは、下記のエラーが発生する原因およびエラーの対処法について解説しました!
ValueError: The Xxxx could not be created because the data didn't validate.
フォームに妥当でない値を入力して送信すると、このエラーが発生する場合があります。
特に妥当性の検証について理解していない状態で ModelForm
クラスの save
メソッドを実行した際に、このエラーが発生しやすいのではないかと思います。
Django 学習中の方であれば、今後妥当性の検証については学ぶ機会があると思いますので、まずはフォームに妥当な値を入力するようにしてエラーを回避すれば良いと思います。
下記のあたりを頭に入れておけば、このエラーが発生した際にも戸惑わなくなると思いますので、とりあえずここだけ覚えておきましょう!
- ウェブブラウザでも妥当性の検証が行われるが、
ModelForm
クラスのsave
メソッドでも妥当性の検証が行われる ModelForm
クラスのsave
メソッドの中で妥当でないと判断されるとエラーが発生してしまうModel
クラスのsave
メソッドでは妥当性の検証が行われない
逆に Django を使いこなしているつもりでこのエラーが発生してしまったという方は、妥当性の検証(バリデーターなど)について学習するのが良いと思います!