前提
python:3.11
django:5.1
問題の概要
- 対象となるモデルクラス(社員モデル):複合主キー制約を設定している & Primary keyがない
- django側に「更新」であることを気付いてもらうためModelFormにinstanceを渡したいが、社員モデルにprimary keyが設定されていないためエラーになる(通常instanceを渡せばdjangoが「更新」であることを認識してくれてpkのバリデーションを突破できる)
- ModelFormにinstanceを渡さないと、社員モデルの更新時にModelFormのバリデーションで複合主キー制約にひっかかり更新できない
実際のコード
class MstStaff(models.Model)
staff_code = models.CharField()
section = models.CharField()
class Meta:
constraints = [models.UniequeConstraint(fields=['staff_code', 'section'], name='unieque_staff')
class InputForm(forms.ModelForm):
staff_code = models.CharField()
section = models.CharField()
class Meta:
model = MstStaff
fields = '__all__'
class UpdateView(View):
def post(self, request):
form = InputForm(request.POST)
if form.is_valid():
# ここでバリデーションを突破できない
# update処理(省略)
複合主キー制約に引っかかる直接の原因
full_cleanメソッドのModel.validate_constraints() が呼び出されるため
解決策
ModelFormでfull_cleanメソッドをオーバーライドし、一意制約のバリデーションをスキップする
class InputForm(forms.ModelForm):
# ...既存コード
# 以下を追加
def full_clean(self):
self.instance.full_clean = lambda *args, **kqargs: None
super().full_clean()
解説
-
Model バリデーション (Model.full_clean()) は、フォームバリデーションのステップ内でフォームの clean() メソッドが呼ばれたすぐ直後に実行されます ModelFormの検証
-
full_cleanメソッドではModel.clean_fields()、Model.clean()、 Model.validate_unique() (validate_unique が True の場合)、そして Model.validate_constraints() (validate_constraints が True の場合) をこの順序で呼び出し、4 つ全てのステージでのエラーを含む message_dict 属性を持つ ValidationError を発生させます。 full_clean() 、 Model.validate_unique()
-
その為、ModelFormクラスでfull_cleanメソッドを無効化して、解決しました
備考
- モデルクラスにpkをつければいいのでは?と言われると思いますが、システム要件的にそれは出来ず。。
- フォームで複合主キー制約のバリデーションが必要であれば、cleanメソッドを手動で追加すればいいと思います