ApathyBear
ApathyBear

Reputation: 9595

Django request.POST as an argument of a form

I am having a hard time wrapping my head around what request.POST is doing as a argument in the following example:

def addauthorView(request):
if request.method == 'POST':
     form = ContactForm(request.POST)
     if form.is_valid():
        first_name = form.cleaned_data['firstname']
        last_name = form.cleaned_data['lastname']
        user_email = form.cleaned_data['email']
        c = AuthorModel(firstname=first_name, lastname=last_name, email=user_email)
        c.save()
        return HttpResponseRedirect('thanks/')
     else:
        form = ContactForm(request.POST)
        return render(request, 'addauthor.html', {'form': form})

So I know that this works, but for some reason I cannot understand the magic that is happening with form = ContactForm(request.POST). Why does the ContactForm need the request.POST argument? What is happening behind the scenes?

Extra question, why is form = ContactForm(request.POST) then repeated in the else: block. Why is that helpful and when is that useful? Examples?

Upvotes: 5

Views: 9879

Answers (1)

patsweet
patsweet

Reputation: 1558

In a nutshell, request.POST is simply the data that was sent when the form was submitted. It's a dictionary of what the user submitted for firstname, lastname and email in your code sample. For those that come from a PHP background, it's what is provided in $_POST.

form = ContactForm(request.POST) binds the data to the form class so Django can do fun stuff like validate inputs with is_valid().

Why then, would you add request.POST to the else: block? Well, have you ever submitted a form to a website and when there was an error you had to completely fill out the form again? That's a crappy user experience, right? By sending the form back to the user with the data from request.POST, you can re-render what the user inputed - along with helpful extras such as error messages - so they can fix them and resubmit.

EDIT: To expand, here is the init method from the BaseForm class in Django:

def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
             initial=None, error_class=ErrorList, label_suffix=None,
             empty_permitted=False):
    self.is_bound = data is not None or files is not None
    self.data = data or {}
    self.files = files or {}
    self.auto_id = auto_id
    self.prefix = prefix
    self.initial = initial or {}
    self.error_class = error_class
    # Translators: This is the default suffix added to form field labels
    self.label_suffix = label_suffix if label_suffix is not None else _(':')
    self.empty_permitted = empty_permitted
    self._errors = None  # Stores the errors after clean() has been called.
    self._changed_data = None

    # The base_fields class attribute is the *class-wide* definition of
    # fields. Because a particular *instance* of the class might want to
    # alter self.fields, we create self.fields here by copying base_fields.
    # Instances should always modify self.fields; they should not modify
    # self.base_fields.
    self.fields = copy.deepcopy(self.base_fields)

When you pass request.POST to your form class, you're really doing data=request.POST. That in turn triggers the self.is_bound = True

Upvotes: 12

Related Questions