novon
novon

Reputation: 993

Passing request object from view to form in Django

I'm trying to create an account edit page which visually contains a single form (ie single submit button) but the fields are part of two (or more) different models. I've pieced together a solution from several SO answers and the form loads fine on GET requests but I would now like to conditionally hide/display the terms of service checkbox field based on what url is being accessed. Specifically on registration the TOS should be displayed while it should not on the account edit page. Simplified code looks like so:

# views.py

class _RequestPassingFormView(FormView):
    http_method_names = ['get', 'post', 'head', 'options', 'trace']

    def get(self, request, *args, **kwargs):
        # Pass request to get_form_class and get_form for per-request
        # form control.
        form_class = self.get_form_class(request)
        form = self.get_form(form_class)
        return self.render_to_response(self.get_context_data(form=form))

    def post(self, request, *args, **kwargs):
        # Pass request to get_form_class and get_form for per-request
        # form control.
        form_class = self.get_form_class(request)
        form = self.get_form(form_class)
        if form.is_valid():
            # Pass request to form_valid.
            return self.form_valid(request, form)
        else:
            return self.form_invalid(form)

    def get_form_class(self, request=None):
        return super(_RequestPassingFormView, self).get_form_class()

    def get_form_kwargs(self, request=None, form_class=None):
        return super(_RequestPassingFormView, self).get_form_kwargs()

    def get_initial(self, request=None):
        return super(_RequestPassingFormView, self).get_initial()

    def get_success_url(self, request=None, user=None):
        # We need to be able to use the request and the new user when
        # constructing success_url.
        return super(_RequestPassingFormView, self).get_success_url()

    def form_valid(self, form, request=None):
        return super(_RequestPassingFormView, self).form_valid(form)

    def form_invalid(self, form, request=None):
        return super(_RequestPassingFormView, self).form_invalid(form)


class AccountEditView(_RequestPassingFormView):
    form_class = AccountEditForm
    template_name = 'account_edit.html'

    def form_valid(self, request, form):
        success_url = self.get_success_url(request, new_user)

        try:
            to, args, kwargs = success_url
            return redirect(to, *args, **kwargs)
        except ValueError:
            return redirect(success_url)

    def get_success_url(selfself,request, user):
        return '/account'




#forms.py

class CombinedFormBase(forms.Form):
    form_classes = []

    def __init__(self, *args, **kwargs):
        super(CombinedFormBase, self).__init__(*args, **kwargs)
        for f in self.form_classes:
            name = f.__name__.lower()
            setattr(self, name, f(*args, **kwargs))
            form = getattr(self, name)
            self.fields.update(form.fields)
            self.initial.update(form.initial)

    def is_valid(self):
        isValid = True
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            if not form.is_valid():
                isValid = False
        # is_valid will trigger clean method
        # so it should be called after all other forms is_valid are called
        # otherwise clean_data will be empty
        if not super(CombinedFormBase, self).is_valid() :
            isValid = False
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            self.errors.update(form.errors)
        return isValid

    def clean(self):
        cleaned_data = super(CombinedFormBase, self).clean()
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            cleaned_data.update(form.cleaned_data)
        return cleaned_data


class RegistrationForm(forms.Form):
    required_css_class = 'required'
    email = forms.EmailField(label=_('E-mail'))
    password1 = forms.CharField(widget=forms.PasswordInput,
                                label=_('Password'))
    password2 = forms.CharField(widget=forms.PasswordInput,
                                label=_('Password (again)'))

    """
    Conditionlly display TOS checkbox based on context

    """
    def __init__(self, *args, **kwargs):
        """
        add in a field for terms of service here if viewing
        the registration form
        """

        super(RegistrationForm, self).__init__(*args, **kwargs)

class AccountProfileForm(forms.Form):
    required_css_class = 'required'
    company = forms.CharField(label=('Company Name'))


class AccountEditForm(CombinedFormBase):
    form_classes = [RegistrationForm, AccountProfileForm]

This is my first django project so it's possible that this is a completely wrong direction also. If so a hint towards a simpler solution would be appreciated.

Upvotes: 1

Views: 5903

Answers (3)

ruddra
ruddra

Reputation: 52028

As you are using class based view, I think you can try like this:

view:

class _RequestPassingFormView(FormView):
    http_method_names = ['get', 'post', 'head', 'options', 'trace']

    def get(self, request, *args, **kwargs):
       form_class = self.get_form_class()
       self.object = None
       form = form_class(request_data=request)
       return self.render_to_response(self.get_context_data(form=form))

Or like this:

class _RequestPassingFormView(FormView):
    http_method_names = ['get', 'post', 'head', 'options', 'trace']

    #No need to override the get method in this view.


    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs.update(request_data=self.request)
        return kwargs

And form is like this:

class RegistrationForm(forms.Form):

   #form fields 

     def __init__(self, *args, request_data=None, **kwargs):
        super().__init__(*args, **kwargs)
        print(request_data)
        # do other operations

Upvotes: 4

dyagmin
dyagmin

Reputation: 304

You may want to consider just having two separate forms. If the only difference is the one on the registration page has a terms of service checkbox, then that form class could inherit from the other class and have the extra form field.

Upvotes: 0

Adrian Lopez
Adrian Lopez

Reputation: 2887

I'm gonna keep it super simple. If you need further details, please refer here:

def my_view(request):
    if request.method == 'POST':
        # Sending your request info as kwarg.
        form = myform(request.POST, user=request.user)


class MyForm(forms.Form):
    def __init__(self, *args, **kwargs):
        # Recieving it.
        self.user = (kwargs.pop('user', None))
        super(MyForm, self).__init__(*args, **kwargs)

Upvotes: 2

Related Questions