7

フォームが送信されたときに、ForeignKey フィールドのモデルのインスタンスを自動的に作成する方法を理解するのに苦労しています。問題を説明する簡単なおもちゃの Web サイトを次に示します。

Model1 と Model2 の 2 つのモデルがあります。Model2 には、Model1 への ForeignKey が含まれています。Model1 のインスタンスを特に選択して ForeignKey に格納するか、その値を空白のままにして Model1 のインスタンスを自動的に生成することにより、ユーザーが Model2 のインスタンスを作成できるようにしたいと考えています。

これが、そのコードがどのように見えるべきだと私が感じるかです。私の models.py コードは非常に単純です。

# models.py
from django.db import models
from django.core.validators import MinValueValidator


class Model1(models.Model):

    # Note this field cannot be negative
    my_field1 = models.IntegerField(validators=[MinValueValidator(0)])


class Model2(models.Model):
    # blank = True will make key_to_model1 not required on the form,
    # but since null = False, I will still require the ForeignKey
    # to be set in the database.
    related_model1 = models.ForeignKey(Model1, blank=True)

    # Note this field cannot be negative
    my_field2 = models.IntegerField(validators=[MinValueValidator(0)])

forms.py は少し複雑ですが、何が起こっているかは非常に簡単です。Model2Form が Model1 のインスタンスを受信しない場合、clean メソッドで自動的に作成を試みて検証し、有効な場合は保存します。有効でない場合は、例外が発生します。

#forms.py
from django import forms
from django.forms.models import model_to_dict

from .models import Model1, Model2


# A ModelForm used for validation purposes only.
class Model1Form(forms.ModelForm):
    class Meta:
        model = Model1


class Model2Form(forms.ModelForm):
    class Meta:
        model = Model2

    def clean(self):
        cleaned_data = super(Model2Form, self).clean()

        if not cleaned_data.get('related_model1', None):

            # Don't instantiate field2 if it doesn't exist.
            val = cleaned_data.get('my_field2', None)
            if not val:
                raise forms.ValidationError("My field must exist")

            # Generate a new instance of Model1 based on Model2's data
            new_model1 = Model1(my_field1=val)

            # validate the Model1 instance with a form form
            validation_form_data = model_to_dict(new_model1)
            validation_form = Model1Form(validation_form_data)

            if not validation_form.is_valid():
                raise forms.ValidationError("Could not create a proper instance of Model1.")

            # set the model1 instance to the related model and save it to the database.
            new_model1.save()
            cleaned_data['related_model1'] = new_model1

        return cleaned_data

ただし、このアプローチは機能しません。フォームに有効なデータを入力すると、正常に機能します。ただし、ForeignKey に何も入力せず、整数に負の値を入力すると、ValueError が返されます。

トレースバック: ファイル "/Library/Python/2.7/site-packages/django/core/handlers/base.py" in get_response 111. response = callback(request, *callback_args, **callback_kwargs) File "/Library/Python/2.7 /site-packages/django/views/generic/base.py" in view 48. return self.dispatch(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/ディスパッチ 69 の generic/base.py". return handler(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/edit.py" in post 172. return super(BaseCreateView, self).post(request, *args, **kwargs) ファイル "/Library/Python/2.7/site-packages/django/views/generic/edit.py" in post 137. if form.is_valid( ): ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in is_valid 124. bool(self.errors) ではなく self.is_bound を返す ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py " in _get_errors 115. self.full_clean() ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() ファイル "/Library/Python/2.7/site -packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) ファイル "/Library/Python/2.7/site-packages/django/ f.save_form_data(instance,cleaned_data[f.name]) ファイル「/Library/Python/2.7/site-packages/django/db/models/fields/」bool(self.errors) ではなく self.is_bound を返します ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in _get_errors 115. self.full_clean() ファイル "/Library/Python/2.7 /site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() ファイル "/Library/Python/2.7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) ファイル "/Library/Python/2.7/site-packages/django/forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[ f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/bool(self.errors) ではなく self.is_bound を返します ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in _get_errors 115. self.full_clean() ファイル "/Library/Python/2.7 /site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() ファイル "/Library/Python/2.7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) ファイル "/Library/Python/2.7/site-packages/django/forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[ f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/py" in _get_errors 115. self.full_clean() ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() ファイル "/Library/Python/2.7/ _post_clean 309 の site-packages/django/forms/models.py" /forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/py" in _get_errors 115. self.full_clean() ファイル "/Library/Python/2.7/site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() ファイル "/Library/Python/2.7/ _post_clean 309 の site-packages/django/forms/models.py" /forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) ファイル "/Library/Python/2.7/site-packages /django/forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) ファイル "/Library/Python/2.7/site-packages /django/forms/models.py" in construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/init .py" in save_form_data 454. setattr(instance, self.name, data) ファイル "/Library/Python/2.7/site-packages/django/db/models/fields/related.py" in set 362. (instance.py ) _meta.object_name、self.field.name))

例外の種類: ValueError at /add/ 例外の値: 割り当てできません なし: "Model2.related_model1" は null 値を許可しません。

つまり、Django が ValidationError をキャッチし、検証が失敗しても Model2 のインスタンスを作成しているということです。

エラーが発生した場合に Model2 のインスタンスを作成しないように _post_clean メソッドをオーバーライドすることで、これを修正できます。しかし、その解決策は醜いです。特に、_post_clean の動作は一般的に非常に役立ちます。より複雑なプロジェクトでは、他の理由で _post_clean を実行する必要があります。

また、ForeignKey を null にすることもできますが、実際には null に設定しないでください。しかし、繰り返しますが、それは悪い考えのようです。

試行された新しい Model1 の検証が失敗するたびに使用するダミーの Model1 をセットアップすることもできますが、それもハックのようです。

一般に、これを修正するための多くのハックを考えることができますが、合理的にクリーンで Pythonic な方法でこれを修正する方法がわかりません。

4

1 に答える 1