LoginSignup
0
2

More than 1 year has passed since last update.

メモ〜CreateViewやUpdateViewで外部キーを設定する方法

Posted at

今回のお題

今回は、CreateViewUpdateViewなどの汎用ビューでインスタンスの外部キー由来のフィールドに値をセットする方法をまとめます。

やりたいことの例

以下のようなモデル設計で、Menuインスタンスの作成時にログイン中のユーザーが所属している店舗を外部キーとして設定したい。

class CustomUser(AbstractUser):
  shop = models.Foreignkey("shop.Shop", on_delete=models.PROTECT)
class Menu(models.Model):
  name = models.CharField(max_length=20)
  shop = models.Foreignkey("shop.Shop", on_delete=models.CASCASE)
class MenuForm(ModelForm):
  class Meta:
    model = Menu
    fields = ["name"]

フォームのフィールドとして指定してしまうと他の店舗を選択できてしまうので、その方法は取りたくない(バリデーションで対応できなくもないが、できれば自動で店舗情報が設定される形にしたい)。

結論

CreateViewUpdateViewに実装されているform_validメソッドをオーバーライドすることで対応可能。

class MenuCreateView(CreateView):
  template_name = "menu/create.html"
  model = Menu
  form_class = MenuForm
  def form_valid(self, form):
    form.instance.shop = self.request.user.shop

form_validメソッドはFormクラスやModelFormクラスのバリデーションがOKだった時に呼ばれてフォームを保存したのちにHttpResponseオブジェクトを返すメソッド。

なのでこのメソッドを上書きし、保存の直前に外部キーのデータがセットされるようにすれば良い。

フォームには外部キーのフィールド自体を用意していないので、店舗情報がなくてもバリデーションに引っかかることはない。

form_validメソッドについて

結論だけでは面白くないので、form_validメソッドの解説を一応。

CreateViewとUpdateViewはそれぞれ、

FormMixin > ModelFormMixin > BaseCreate(Update)View > Create(Update)View

というルートで継承を重ねており、その度にform_validメソッドに関する機能が追加されている。

具体的には、

  • FormMixinでform_validメソッドを定義(この段階ではHttpResponseオブジェクトを返す役割のみ)。
  • ModelFormMixinで、form_validメソッドが上書きされ、HttpResponseを返す前にフォーム内容が保存されるようになる。
  • ProcessFormView(BaseCreate/Updateviewのもう一つの継承元)のpostメソッド内で、form.is_valid()=trueの場合にのみform_validメソッドが呼び出されるように定義される。

という流れになっている。

class FormMixin(ContextMixin):
  def form_valid(self, form):
        """If the form is valid, redirect to the supplied URL."""
        return HttpResponseRedirect(self.get_success_url())
class ModelFormMixin(FormMixin, SingleObjectMixin):
  def form_valid(self, form):
        """If the form is valid, save the associated model."""
        self.object = form.save()
        return super().form_valid(form)
class ProcessFormView(View):
    """Render a form on GET and processes it on POST."""
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        return self.render_to_response(self.get_context_data())

    def post(self, request, *args, **kwargs):
        """
        Handle POST requests: instantiate a form instance with the passed
        POST variables and then check if it's valid.
        """
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    # PUT is a valid HTTP verb for creating (with a known URL) or editing an
    # object, note that browsers only support POST for now.
    def put(self, *args, **kwargs):
        return self.post(*args, **kwargs)
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2