Friday, December 22, 2017

How can I combine two views and two forms into one single template?

Leave a Comment

I have two separate class-based views and would like to keep their functionality, and have the two CBVs point to the same template (I'm trying to bring two separate forms into a single page).

More specifically, I am trying to subclass/combine this email view, and this password change view, so that I can have them point to the same template, as I ultimately would like both forms on the same page.

I've attempted to do this by subclassing them into my own views:

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)   

But I am now finding out due to errors, one by one, that it appears nothing from the parent class is actually transferred over to my custom class unless I manually bring it in(success_url, methods, etc). Even then, the code from the original classes that I am subclassing is pointing elsewhere.
SO, when combining these two views, do I need to copy all of the original code into my custom subclass views?

Is this the proper way to accomplish it? How can I combine these two views? I'm ultimately looking for a way to have both of their forms on a single page in my own app. Is there possibly a simpler way to accomplish this using the library's provided templates?

3 Answers

Answers 1

You can use Django Multi Form View to combine many forms in view

After install you can use it like below:

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) 

and in template:

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

Answers 2

This can be solved by implementing a (kind of) partial update method for your view.

Tools:

First protect your sensitive data:

  1. sensitive_variables decorator:

    If a function (either a view or any regular callback) in your code uses local variables susceptible to contain sensitive information, you may prevent the values of those variables from being included in error reports using the sensitive_variables decorator

  2. sensitive_post_parameters() decorator:

    If one of your views receives an HttpRequest object with POST parameters susceptible to contain sensitive information, you may prevent the values of those parameters from being included in the error reports using the sensitive_post_parameters decorator

Create some code after:

  1. Use ModelForm to create a form with specific fields of interest for your view. Don't include the password field, because we will add it in a different way to suit our needs:

    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',...) 

    Here you can use exclude instead of fields if you want to keep every other field except the password: ex. exclude = ('password',)

  2. Use an UpdateView to handle the hustle and override it's form_valid method to handle the partial update:

    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. Finally, create a url for that view:

    myapp/urls.py:

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

This way you can handle:

  • Email AND password update at the same time
  • Only email update.
  • Only password update.

Using code from this solution: Django: Only update fields that have been changed in UpdateView

Answers 3

I think You can use the default class in django to a achieve the same result. As far as i understood i got the scenario like this we have two django forms and we need it to be used in same template if that is the scenario we can use the LoginView from django.contrib.auth.views which has several customizable option like you can give the additional form like this

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

it will be using the get context data method to update the form which you will be able to get in the template and use accordingly .If you are not wishing to use the code like this you can still use it like this

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) 

Then you can handle your post in the form valid accordingly as your requirement. Hope that helps get back if you need any additional requirement.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment