Django forms¶
- In web development, forms are fundamental components that facilitate user interaction and data submission.
- Django, a powerful web framework in Python, provides a robust mechanism for handling forms efficiently.
HTML forms¶
- an HTML form is an essential part of web development, enabling users to input data and submit it to a server.
- HTML forms consist of various elements such as text inputs, checkboxes, radio buttons, and dropdowns, defined using HTML tags like
<form>
,<input>
,<textarea>
, and<select>
.
login.html
<form action="/submit/" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Submit</button>
</form>
In this basic example:
<form>
defines the form.<input>
withtype="text"
creates a text input field.<input>
withtype="password"
creates a password input field.required
attribute enforces field validation on the client side.
Django forms¶
- Django abstracts the complexity of form handling in web applications by providing Django forms.
- These are Python classes that generate HTML forms based on model definitions or custom form logic
Django provides two types of forms:
- Forms: Used to create forms not directly linked to models.
- ModelForms: Used to create forms that are directly linked to models.
Creating a basic form¶
- import django forms -
from django import forms
- define a form class
from django import forms
class LoginForm(forms.Form):
username = forms.EmailField(max_length=63, label='Email')
password = forms.CharField(widget=forms.PasswordInput, label='Password')
forms.CharField
field generates HTML input field when renderedforms.PasswordInput
widget generates HTML input withtype="password"
- more form fields can be find at django official documentation
Get HTML from Django Form¶
- Django form has below render methods
form as div tag - as_div()
¶
form = LoginForm()
html = form.as_div()
print(html)
form.as_div()
generates below html code
<div>
<label for="id_username">Username:</label>
<input type="email" name="username" maxlength="63" required id="id_username">
</div>
<div>
<label for="id_password">Password:</label>
<input type="password" name="password" required id="id_password">
</div>
form as p tag - as_p()
¶
form = LoginForm()
html = form.as_p()
print(html)
form.as_p()
generates below html code <p>
<label for="id_username">Username:</label>
<input type="email" name="username" maxlength="63" required id="id_username">
</p>
<p>
<label for="id_password">Password:</label>
<input type="password" name="password" required id="id_password">
</p>
form as ul tag - as_ul()
¶
form = LoginForm()
html = form.as_ul()
print(html)
form.as_ul()
generates below html code
<li>
<label for="id_username">Username:</label>
<input type="email" name="username" maxlength="63" required id="id_username">
</li>
<li>
<label for="id_password">Password:</label>
<input type="password" name="password" required id="id_password">
</li>
form as table tag - as_table()
¶
form = LoginForm()
html = form.as_table()
print(html)
form.as_table()
generates below html code
<tr>
<th><label for="id_username">Username:</label></th>
<td>
<input type="email" name="username" maxlength="63" required id="id_username">
</td>
</tr>
<tr>
<th><label for="id_password">Password:</label></th>
<td>
<input type="password" name="password" required id="id_password">
</td>
</tr>
Render form in a template¶
- Create a simple view that uses
LoginForm
my_app/views.py
from django.shortcuts import render
from .forms import LoginForm
def login_view(request):
context = {"form": LoginForm()}
return render(request, "login.html", context)
- use the form with template
templates/login.html
<form action="" method="post">
{% csrf_token %}
{{ form.as_div }}
<input type="submit" value=Submit">
</form>
- other html form methods like
as_p
,as_ul
,as_table
can also be used. csrf_token
template tag generates a secure random token that prevents prevent CSRF attacks
Working with form data¶
django form - initial data¶
- We need to pass the initial data data to django form using kwarg
initial
like below.
from .forms import LoginForm
initial = {"username": "rockstar007"}
form = LoginForm(initial=initial)
print(form.as_p())
- It generates below HTML code
<p>
<label for="id_username">Username:</label>
<input type="email" name="username" value="rockstar007" maxlength="63" required id="id_username">
</p>
<p>
<label for="id_password">Password:</label>
<input type="password" name="password" required id="id_password">
</p>
- from the above HTML code, we can find that the
username
input has valuerockstar007
.
django form - validate data¶
- django forms also used to validate the data
- when user submits the data to the app, it can be validated like below.
data = {"username": "rockstar007", "password": "secret"}
form = LoginForm(data=data)
is_form_valid = form.is_valid()
print(is_form_valid)
- above code gives the output as
False
. - because, the username is an email field it expects the value to be an email.
- we can have custom field validations as well.
django form - detect data changes¶
- If we want to find the initial data changed when form submitted then we can detect it.
- It can be done using the attribute -
is_changed
initial_data = {"username": "wow@email.com", "password": "secret"}
data = {"username": "user@email.com", "password": "secret"}
form = LoginForm(data=data, initial=initial_data)
is_form_changed = form.has_changed()
print(is_form_changed)
- Above code gives the output as
True
. Because, thedata
is different frominitial
data.
django form - custom field validation¶
- we can add field-level validation to forms by defining methods that start with
clean_
followed by the name of the field. - lets look at the form below
from django import forms
class LoginForm(forms.Form):
username = forms.EmailField(max_length=63, label='Email')
password = forms.CharField(widget=forms.PasswordInput, label='Password')
def clean_password(self):
password = self.cleaned_data.get('password')
if len(password) < 8:
raise forms.ValidationError('Password must be at least 8 characters long.')
return password
- above form will raise the error if the
password
value length is below 8 characters is_valid()
method is used to validate the form data.
django form - custom data validation¶
clean
method is used to add validation that depends on multiple fields or other logic that doesn't fit into a single field's validation.- let's look at the example below.
from django import forms
class RegistrationForm(forms.Form):
username = forms.CharField(max_length=100)
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password and confirm_password:
if password != confirm_password:
raise forms.ValidationError('Passwords do not match.')
return cleaned_data
- above form validates the form data and raises validation error if
password
doesn't match withconfirm_password
. is_valid()
method is used to validate the form data.
django form - form errors¶
- if the form is not valid then it will raise the validation errors.
- we can use the attribute
errors
to get the form errors.
from django import forms
class RegistrationForm(forms.Form):
username = forms.CharField(max_length=100)
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password and confirm_password:
if password != confirm_password:
raise forms.ValidationError('Passwords do not match.')
return cleaned_data
data = {"username": "rockstart007", "password": "secret", "confirm_password": "sec"}
form = RegistrationForm(data=data)
is_valid = form.is_valid()
print(form.errors)
- It will give the below errors
<ul class="errorlist"><li>__all__<ul class="errorlist nonfield"><li>Passwords do not match.</li></ul></li></ul>
- To get errors as json then use the method
forms.errors.as_json()
Rendering form fields in template¶
Iterate over form fields¶
- Lets consider the form
RegistrationForm
- Can use
for
loop to iter through form fields
<div>
<p>{{ form.non_field_errors }}</p>
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
</div>
form.non_field_errors
- renders non-field error messagesfield.errors
- renders error messages if anyfield.label_tag
- renders label tagfield
- renders the form field [i.e input, textarea, select, etc.]
Manually render form fields¶
- Lets consider the form
RegistrationForm
<div>
<!-- non field errors -->
{{ form.non_field_errors }}
<!-- username -->
<div class="fieldWrapper">
{{ form.username.errors }}
<label for="{{ form.username.id_for_label }}">Username</label>
{{ form.username }}
</div>
<!-- password -->
<div class="fieldWrapper">
{{ form.password.errors }}
<label for="{{ form.password.id_for_label }}">Password</label>
{{ form.password }}
</div>
<!-- confirm password -->
<div class="fieldWrapper">
{{ form.confirm_password.errors }}
<label for="{{ form.password.id_for_label }}">Confirm Password</label>
{{ form.confirm_password }}
</div>
</div>