今回のお題
今回はdjangoのclean
メソッドを使う上での注意点として、自分が引っかかった点を挙げておきます。
一つのフォームにclean
メソッドとclean_xxx
メソッドが両方あって、cleanメソッドの方でもxxx
フィールドを参照している場合に起こりがちなエラーになります。
エラーが起きた時の状況
モデルとフォームについては以下の通りで、開始日と終了日を選択してキャンペーン情報を登録するという機能を実装していました(関係のない部分については省略しています)。
from django.db import models
# Create your models here.
class Campaign(models.Model):
start = models.DateField(
verbose_name="開始日"
)
end = models.DateField(
verbose_name="終了日"
)
from django.core.exceptions import ValidationError
from .models import Campaign
from django.forms import ModelForm, SelectDateWidget
import datetime
class CampaignForm(ModelForm):
class Meta:
model = Campaign
fields = ["menu","campaign_type", "value", "start", "end"]
widgets = {
"start": SelectDateWidget,
"end": SelectDateWidget,
}
def clean_start(self):
start = self.cleaned_data.get("start")
if start <= datetime.date.today():
raise ValidationError("開始日は本日以降の日付で入力してください")
return start
def clean(self):
cleaned_data = super().clean()
start = cleaned_data.get("start")
end = cleaned_data.get("end")
if end <= start:
raise ValidationError("終了日は開始日よりも後の日付で入力してください")
return cleaned_data
まずclean_start
で開始日が今日よりも前になっていないかどうかを確認した後で、clean
メソッドで終了日が開始日以前でないかどうかを確認しています。
発生したエラー
上記のコードを書いた上で、clean_start
のテストのために開始日=昨日の日付',
終了日=翌日の日付`と入力したところ以下のエラーが。
TypeError at /campaign/create/
'<=' not supported between instances of 'datetime.date' and 'NoneType'
# 中略
Exception Location: /Users/hashimotokeishi/cakeshop/cakeshop/campaign/forms.py, line 40, in clean
'<='を用いてdatetimeオブジェクトとNoneTypeを比較することはできないよ。
該当箇所はforms.pyの40行目だよ。
とのこと。
で、該当箇所を見てみると
def clean(self):
cleaned_data = super().clean()
start = cleaned_data.get("start")
end = cleaned_data.get("end")
if end <= start: # ここが40行目
raise ValidationError("終了日は開始日よりも後の日付で入力してください")
return cleaned_data
の一文がありました。
そもそもclean
メソッドはclean_xxx(今回であればclean_start)
の後に呼ばれます。
今回の入力内容であればclean_start
でキャンペーン開始日時が弾かれてしまうので、そもそもcleaned_data.get("start")
の戻り値はNone
です。
なのでキャンペーン終了日時とNone
を比較することになり、上記のようなエラーが発生してしまったのでした。
解決策
以下のように、startの値が存在しているかどかの確認をしましょう。
if start and start >= end: