【问题标题】:Unable to figure out how ModelForm is rendered using generic UpdateView无法弄清楚如何使用通用 UpdateView 呈现 ModelForm
【发布时间】:2018-10-29 23:03:56
【问题描述】:

我试图弄清楚当我使用通用 UpdateView 时如何实例化 ModelForm。

我浏览了 django 源代码并查看了 UpdateView 和相关的 Form 类,但我看不到任何将 instance 明确传递给 ModelForm 类的对象的代码行.

例如,假设我们有PostForm 作为ModelForm,那么我们会这样写:

form = PostForm(instance=Post.object.get(pk=pk))

从模型对象呈现表单。

我在 django 源代码中看不到类似的代码,也无法弄清楚在 UpdateView 的情况下通用 ModelForm 是如何填充的 即,当我在 POST 表单后提交数据时,我的表单的 self.instance 属性如何被实例化。

【问题讨论】:

    标签: django django-models django-forms django-templates django-views


    【解决方案1】:

    ModelForminstance 属性在get_form_kwargs() 中实例化,在ModelFormMixin 中定义

    详细解释见下:

    UpdateView 视图继承 SingleObjectTemplateResponseMixin, BaseUpdateView

    BaseUpdateView进一步继承ModelFormMixinProcessFormView
    它还定义了通过 dispatch
    调用的 getpost 方法 这些getpost 方法将object 属性设置为当前模型对象。下面是来自 django 文档的代码 sn-p:

    class BaseUpdateView(ModelFormMixin, ProcessFormView):
        """
        Base view for updating an existing object.
    
        Using this base class requires subclassing to provide a response mixin.
        """
        def get(self, request, *args, **kwargs):
            self.object = self.get_object()
            return super(BaseUpdateView, self).get(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            self.object = self.get_object()
            return super(BaseUpdateView, self).post(request, *args, **kwargs)
    

    get 和 post 方法也调用父级的 get 和 post 方法,即在 ProcessFormView 中定义的 get 和 post

    在 GET 请求期间
    ProcessFormView 中定义的get 方法调用在FormMixin 下覆盖的get_context_data(),它进一步调用get_form() 以返回要在视图中使用的表单实例。

    def get_context_data(self, **kwargs):
        """
        Insert the form into the context dict.
        """
        if 'form' not in kwargs:
            kwargs['form'] = self.get_form()
        return super(FormMixin, self).get_context_data(**kwargs)
    

    get_form() 调用位于ModelFormMixinFormMixin 中的get_form_kwargs(),但由于ModelFormMixin 继承自FormMixin,因此ModelFormMixin 中定义的方法将覆盖FormMixin 中定义的方法。这个get_form_kwargs() 方法首先调用父/父的方法,然后将表单的instance 属性设置为当前模型对象,即self.object(或简单的object)。
    以下文档中的代码 sn-p :

    def get_form_kwargs(self): #defined in ModelFormMixin class
        """
        Returns the keyword arguments for instantiating the form.
        """
        kwargs = super(ModelFormMixin, self).get_form_kwargs()
        if hasattr(self, 'object'):
            kwargs.update({'instance': self.object})
        return kwargs
    

    然后使用模型对象的属性呈现表单

    在 POST 请求期间:
    如前所述(参见第一个代码 sn-p),就像get() 一样,post() 方法也将object 属性设置为当前模型对象,即self.object=self.get_object()。 (get_object() 继承自 SingleObjectMixin 类)
    然后它调用ProcessFormViewpost方法,即使用get_form() 方法创建form 实例的父类。 (就像 get_context_method 在get 请求的情况下所做的那样) get_form() 调用get_form_kwargs 进一步将表单的instance 属性设置为在第一个post 方法调用中实例化的self.object
    代码如下:

    class ProcessFormView(View):
    """
    A mixin that renders a form on GET and processes it on POST.
    """
    def get(self, request, *args, **kwargs):
        """
        Handles GET requests and instantiates a blank version of the form.
        """
        return self.render_to_response(self.get_context_data())
    
    def post(self, request, *args, **kwargs):
        """
        Handles POST requests, instantiating a form instance with the passed
        POST variables and then checked for validity.
        """
        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)
    

    接下来,根据基本约束验证表单,这是通过调用从 BaseForm 类继承的 form.is_valid() 方法来完成的。 这是非常重要的一步,因为此时,instance 对象的属性会更新为表单中 POST 的数据。

    这一切都是通过以下调用堆栈实现的:

    form.is_valid() 调用 -> errors 属性 -> 调用 full_clean() -> _clean_fields() -> _clean_form() -> _post_clean()
    _post_clean()POST 构造 instance通过调用construct_instance_method获取数据

    要更好地理解这些函数,请阅读is_valid() hereBaseForm 类和_post_clean() hereBaseModelForm

    【讨论】:

      【解决方案2】:

      我想你可能正在寻找方法 FormMixin.get_form_kwargs().

      这里是来自Githubrepo的源码:

      def get_form_kwargs(self):
          ...
          if self.request.method in ('POST', 'PUT'):
              kwargs.update({
                  'data': self.request.POST,
                  'files': self.request.FILES,
              })
          return kwargs
      

      如您所见,如果请求是 POST,则来自 POST 和 FILES 的数据 从该方法返回,该方法又用于实例化表单,正如您在下面的第二个 sn-p 中从同一 source 看到的:

      def get_form(self, form_class=None):
          ...
          return form_class(**self.get_form_kwargs())
      

      【讨论】:

      • @Raif:有两种情况:
      • 再次查看代码并阅读接受的答案:你是对的,我的答案是不对的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-26
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多