Django Forms and validating input data

To receive data from the user and submit it to the server, we will use forms

Accessing data from html form in python

index.html

<!-- To submit data, method should be post -->
<!-- action specifies the url to which the data is submitted. If nothing is specified, data is sent to the view function linked with index.html -->
  <form  method="post" action="">
      <input type="text" name="username" placeholder="Username">
      <input type="password" name="password" placeholder="Password">
    <input type="submit" name="submit" value="Login">
  </form>

urls.py

path("",views.index,name='index'), #form will be sent to view.index

views.index

def index(request):
        if request.method == 'POST':
            #Get the fields
            username = request.POST.get('username')
            password = request.POST.get('password')

Creating a basic form

Django forms can be used which comes with several conveniences like validators, interaction with database etc

1) Create a form class in app3/forms.py (same as that of model)

from django import forms

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField(widget=forms.Textarea) 
    #Pass on the widget textarea. Otherwise, input looks same as that of name and email

2) Use the form class in views.py

from app3 import forms

def form_name_view(request):
    form = forms.FormName()
    return render(request,'form_page.html',context={'form':form})

3) Inject the form into form_page.html. Note that csrf_token is necessary for posting the form.

<body>

    <h1>Form Page</h1>

    <form method="post">
      
      <!-- {form.as_p} renders the fields in separate lines -->

      {%  csrf_token  %}
      <!-- This is a security token, without which the form breaks -->

    </form>

</body>

Changes in form input attributes can be done through views.py (demonstrated later). It can also be done in the template by creating custom template tags

Accessing the submitted form data

We can access the submited data by grabbing the form with request object in views.py

def form_name_view(request): # Linked with urls.py to get the request object

    if request.method == 'POST':
        form = forms.FormName(request.POST)

        do_something_with_data(form)

    form = forms.FormName()
    return render(request,'form_page.html',context={'form':form})

def do_something_with_data(form):

    if form.is_valid(): #Check if form fields are valid

        # Do Something
        print("Validation success")

        # Access the data
        name = form.cleaned_data['name']
        print("NAME :", name)

Creating custom validation

This is done by creating methods clean_{fieldName} in the form class

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField(widget=forms.Textarea)

    #Each field has certain default validation check.
    #We can also add custom Validation
    #Eg. Lets demonstrate botcather

    botcatcher = forms.CharField(required=False,widget=forms.HiddenInput)
    ##Hidden input widget hides the field from rendering.
    ## But the field will be present in HTML element which a bot uses

    #Define a method starting with clean_<field_name>.
    #Django will automatically look for this method while Validation
    def clean_botcatcher(self):
        botcather = self.cleaned_data['botcatcher']
        if len(botcather):
            raise forms.ValidationError("Gotcha Bot!")
        return botcather

Django's built-in validators can also be used. Look the documentation for complete list of validators

from django.core import validators

##Using django built-in validators
    botcather1 = forms.CharField(required=False,
                                 widget=forms.HiddenInput,
                                 validators=[validators.MaxLengthValidator(0)])

Custom validators can also be passed in through validators argument

#Custom validator can also be passed in through validators arg
    #needs first argument to be 'value'
    def check_for_z(value):
        if value[0].lower() != 'z':
            raise forms.ValidationError("Needs to start with Z")

    refree = forms.CharField(validators=[check_for_z])

Note: Can also be used with model fields

We can also have validators for all fields defined under a single method

# Changing label
    verify_email = forms.EmailField(label="Re-enter email")

    ##We can also have a single validator for all fields at once
    def clean(self):
        #grab all clean data
        all_clean = super().clean()

        ##Prints only clean data
        print(super().clean())

        if 'email' in all_clean and 'verify_email' in all_clean:
            if all_clean['email'] != all_clean['verify_email']:
                raise forms.ValidationError("Make sure emails are same")

Saving Form inputs to a Model

Create model and migrate

In forms.py

Instead of inherting from forms.Forms, we will use forms.ModelForm in forms.py

We then add an inline class called meta which connects the model to form

In views.py

Grab the form and use form.save() to save the form into database

models.py

from django.db import models

# Create your models here.
class SignUp(models.Model):
    name = models.CharField(max_length=264);
    email = models.EmailField();

NOTE: Remember to migrate and register model in admin.py

forms.py

Changes to input attributes are demonstrated

from django import forms
from app3.models import SignUp

class Form_SignUp(forms.ModelForm):
    #Can have validators/other modifications here. Not Compulsory. 
    name = forms.CharField(label="",widget=forms.TextInput(attrs={'placeholder':'Name'}))
    #field names and form names should match

    class Meta:
        model = SignUp #Assign model
        fields = "__all__" # All fields in model
        #exclude = [exclude1,exclude2] #All model fields except
        #fields = (include1,include2) #Only these model fields

views.py

def form_signUp_view(request):
    form = forms.Form_SignUp()

    if request.method == 'POST':
        form = forms.Form_SignUp(request.POST)

        if form.is_valid():
            form.save(commit=True) # Save form to database
        else:
            print("Invalid")

    return render(request,'form_page2.html',context={'form':form})