Search code examples
djangodjango-class-based-viewsformview

Call post function of a class based view using formview


I am new to django class based views and may be the way I am approaching this is a little naive, so I would appreciate if you could suggest a better way.

So my problem is here:

There are three types of users in my project. 1. Student, 2. Teacher, 3. Parent. I need to be able to show different user settings pertaining to each type of user when the user requests the settings page in their respective forms. Also, I need to be able to save the data into the respective tables as the user submits the form.

I have a class based view (UserSettingsView):

class UserSettingsView(LoginRequiredMixin, FormView):
    success_url = '.'
    template_name = 'accts/usersettings.html'

    def get_initial(self):
        if self.request.user.is_authenticated():
            user_obj = get_user_model().objects.get(email=self.request.user.email)
            if user_obj.profile.is_student:
                return {
                    'first_name': user_obj.profile.first_name,
                    'last_name': user_obj.profile.last_name,
                    """ and other student field variables """
                    }
            if user_obj.profile.is_teacher:
                return {
                    """ Teacher field variables """
                    }
        else:
            return render_to_response('allauth/account/login.html')

    def form_valid(self, form):
        messages.add_message(self.request, messages.SUCCESS, 'Settings Saved!')
        return super(UserSettingsView, self).form_valid(form)

    def get_context_data(self, **kwargs):
        context = super(UserSettingsView, self).get_context_data(**kwargs)
        context['user'] = get_user_model().objects.get(email=self.request.user.email)
        context['userprofile'] = UserProfile.objects.get(user_id=context['user'])
        return context

    def post(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        form.full_clean()

        if form.is_valid():
            user = request.user
            user.profile.first_name = form.cleaned_data['first_name']
            user.profile.last_name = form.cleaned_data['last_name']
            user.profile.save()
            if user.profile.is_student:
            """ update student database """
                user.save()
                user.student.save()
            if user.profile.is_teacher:
            """ update teacher database """
                user.save()
                user.teacher.save()

            return self.form_valid(form)
        else:
            return self.form_invalid(form)

Different instances of Usersettings view are called using the pick_settings generic view.

url(regex=r'^profilesettings/',view=pick_settings,name='profilesettings'),

And here is the pick_settings view:

def pick_settings(request):
    if request.user.is_authenticated():
        if request.method == 'GET':
            if request.user.profile.is_student:
                return UserSettingsView.as_view(form_class=StudentSettingsForm)(request)
            if request.user.profile.is_teacher:
                return UserSettingsView.as_view(form_class=TeacherSettingsForm)(request)
            if request.user.profile.is_parent:
                return UserSettingsView.as_view(form_class=ParentSettingsForm)(request)
        else:
            if request.method == 'POST':
                """
         return ***<--- I need to know what to pass here to be able to call the appropriate post function of the UserSettingsView?---->"""***
    else:
            return HttpResponseRedirect('/accounts/login/')

I need to be able to call the post function of the UserSettingsView. May be using the get_context_data? But I am not sure how.

Again it will be great, if someone could suggest a better way because I am pretty sure this might be violating the DRY principle. Although, I am not too concerned with that as long as the job gets done as I am running a deadline. :) Thanks!


Solution

  • FormView has a method get_form_class(). It is called from get() and post(), so self.request will already be set (as will be self.request.user). Consequently,

    class UserSettingsView(LoginRequiredMixin, FormView):
       [...]
       def get_form_class(self):
           # no need to check is_authenticated() as we have LoginRequiredMixin
           if request.user.profile.is_student:
                return StudentSettingsForm
           elif user.profile.is_teacher:
                return TeacherSettingsForm
           elif user.profile.is_parent:
                return ParentSettingsForm
    

    This should already to the trick as you get the correct form for each user type.

    If you also need to render different templates, override get_template_names():

       def get_template_names(self):
           if request.user.profile.is_student:
                return ['myapp/settings/student.html']
           elif user.profile.is_teacher:
                return ['myapp/settings/teacher.html']
           elif user.profile.is_parent:
                return ['myapp/settings/parent.html']
    

    DRY can be achieved using proper inheritance in the templates combining common template fragments.

    And lest I forget (I already forgot): To get rid of the if in the post() method of your view, simple override the save() method of you forms which I assume are ModelForms, anyway.