【问题标题】:How can I combine two views and two forms into one single template?如何将两个视图和两个表单合并到一个模板中?
【发布时间】:2018-05-27 03:27:59
【问题描述】:

我有两个单独的基于类的视图,并希望保留它们的功能,并让两个 CBV 指向同一个模板(我试图将两个单独的表单放入一个页面中)。

更具体地说,我正在尝试子类化/组合this email view 和这个password change view,这样我就可以让它们指向同一个模板,因为我最终希望这两个表单都在同一个页面上。

我试图通过将它们子类化到我自己的视图中来做到这一点:

class MyEmailUpdateView(LoginRequiredMixin, EmailView):
    template_name = 'account/account_settings'
    success_url = reverse_lazy('settings')

    def form_valid(self, form):
        return super(SettingsUpdateView, self).form_valid(form)

class MyPasswordUpdateView(LoginRequiredMixin, PasswordChangeView):
    template_name = 'account/account_settings'
    success_url = reverse_lazy('settings')

    def form_valid(self, form):
        return super(SettingsUpdateView, self).form_valid(form)  

但是由于错误,我现在一一发现,除非我手动将其引入(success_url,方法等),否则似乎父类中的任何内容实际上都没有转移到我的自定义类中。即便如此,我子类化的原始类中的代码仍指向其他地方。
那么,当结合这两个视图时,我是否需要将所有原始代码复制到我的自定义子类视图中?

这是完成它的正确方法吗?如何结合这两种观点?我最终正在寻找一种方法,将它们的两个表单都放在我自己的应用程序的一个页面上。使用library's provided templates 是否有更简单的方法来完成此任务?

【问题讨论】:

  • 当您说“我最终希望两个表单都在同一个页面上”时,您的意思是您希望一个 URL 指向包含两个表单的页面吗?
  • @ubadub 一个包含两种形式的页面的单个 URL,是的。

标签: python django


【解决方案1】:

您可以使用Django Multi Form View在视图中组合多个表单

安装后你可以像下面这样使用它:

class MultiFView(MultiFormView):
    form_classes = {
        'email_form' : AddEmailForm,
        'change_password_form' : ChangePasswordForm
    }
    record_id = None
    template_name = 'web/multi.html'

    def forms_valid(self, forms):
        email = forms['email_form'].save(commit=False)
        email.save()
        return super(MultiFView, self).forms_valid(forms)

在模板中:

{{ forms.email_form.as_p }}
{{ forms.change_password_form.as_p }}

【讨论】:

  • 我更喜欢不依赖其他第三方库的方法
  • ...lol..就像荷兰谚语一样:“农民不知道他不吃的东西”.. 正如西蒙所说 ;-)
  • Simon 试试这个库。你为什么要改造自行车?
【解决方案2】:

我认为您可以使用 django 中的默认类来实现相同的结果。 据我了解,我得到了这样的场景,我们有两个 django 表单,如果这是我们可以使用来自django.contrib.auth.views 的 LoginView 的场景,我们需要在同一个模板中使用它,它有几个可自定义的选项,比如你可以给像这样的附加表格

class LoginUserView(LoginView):
    authentication_form = LoginForm
    extra_context = {"register_form": RegistrationForm}
    template_name = 'accounts/index.html'

它将使用获取上下文数据方法来更新您将能够在模板中获取并相应使用的表单。如果您不希望使用这样的代码,您仍然可以这样使用它

class MyEmailUpdateView(LoginRequiredMixin, EmailView):
    form_class = EmailForm
    template_name = 'account/account_settings'
    success_url = reverse_lazy('settings')

    def get_context_data(self, **kwargs):
        context = super(MyEmailUpdateView, self).get_context_data(**kwargs)
        context['password_reset_form']  = ResetPasswordForm
        return context

    def form_valid(self, form):
        return super(MyEmailUpdateView, self).form_valid(form)

然后您可以根据您的要求以有效的形式处理您的帖子。 如果您需要任何其他要求,希望能帮助您回复。

【讨论】:

    【解决方案3】:

    这可以通过为您的视图实现(某种)部分更新方法来解决。

    工具:

    首先保护您的敏感数据:

    1. sensitive_variables装饰者:

      如果代码中的函数(视图或任何常规回调)使用容易包含敏感信息的局部变量,则可以使用sensitive_variables 装饰器阻止这些变量的值包含在错误报告中

    2. sensitive_post_parameters()装饰者:

      如果您的一个视图接收到一个带有 POST 参数的 HttpRequest 对象,该对象可能包含敏感信息,您可以使用sensitive_post_parameters 装饰器阻止这些参数的值包含在错误报告中

    之后创建一些代码:

    1. 使用ModelForm 创建一个表单,其中包含您感兴趣的特定字段。不要包含 password 字段,因为我们会以不同的方式添加它以满足我们的需要:

      myapp/forms.py:

      class PartialUpdateForm(forms.ModelForm):
          new_password = forms.CharField(
              required=False, widget=forms.widgets.PasswordInput
          )
      
          class Meta:
              model = MyUser
              fields = ('email', 'other_fields',...)
      

      如果您想保留除password 之外的所有其他字段,您可以在此处使用exclude 而不是fields:例如。 exclude = ('password',)

    2. 使用UpdateView 处理喧嚣并覆盖它的form_valid 方法来处理部分更新:

      myapp/views.py:

      class MyUpdateView(UpdateView):
          template_name = 'account/account_settings'
          form_class = PartialUpdateForm
          success_url = reverse_lazy('settings')
      
          @sensitive_variables('new_password')
          @sensitive_post_parameters('new_password')
          def form_valid(self, form):
              clean = form.cleaned_data
              new_password = clean.get('new_password')
              if new_password:
                  form.instance.password = hash_password(new_password)
              return super().form_valid(form)
      
    3. 最后,为该视图创建一个 url:

      myapp/urls.py:

      ...
      url(r'^your/pattern/$', MyUpdateView.as_view()),
      

    这样你就可以处理了:

    • 电子邮件和密码同时更新
    • 只有电子邮件更新。
    • 仅密码更新。

    使用此解决方案中的代码:Django: Only update fields that have been changed in UpdateView

    【讨论】:

    • 似乎与我的问题毫无关系。
    • @simonsays 它允许您使用单一视图来处理电子邮件或密码更新(或同时更新两者),然后您可以将其添加到单个 url。但如果你这么认为...
    猜你喜欢
    • 2022-01-03
    • 2013-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多