1: Making a Django form


Our web app currently has a homepage and a products page but let's give users the chance to register and login in order to rate products and create a wishlist.  First we will work on the user registration form and functionality, then we will move on to the services offered to the user once logged in.

In order to add custom forms to our web app, it's best to create a separate forms.py file that will contain all form-related code. While Django does have a built-in user authentication system for registering users, we will extend and customize it to include user emails.

 

Create a new forms.py file

env > mysite > main > (New File) forms.py

Create Django forms.pyGIF

Create the new file in the mysite > main folder.

 

Customize the Django UserCreationForm in forms.py

env > mysite > main > forms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User


# Create your forms here.

class NewUserForm(UserCreationForm):
	email = forms.EmailField(required=True)

	class Meta:
		model = User
		fields = ("username", "email", "password1", "password2")

	def save(self, commit=True):
		user = super(NewUserForm, self).save(commit=False)
		user.email = self.cleaned_data['email']
		if commit:
			user.save()
		return user

Add three Django imports to the top of the page.  The first imports forms from Django while the others import a UserCreationForm and the User model from Django's built-in authentication forms and models.  The NewUserForm takes in Django's pre-built registration form as a parameter since it extends its functionality.  Then the email field is declared.  Next, since the form is based on the User model, the model is specified under class Meta: along with the all of the user fields we would like included in our form.

Finally we write a short function that overwrites the default save function to include the email field we added.  Don't worry if this part is confusing, we just added an email field to our registration form.  








2: Creating a register page


Now that we customized the UserCreationForm, let's create a register HTML template to render the form in the browser page.

 

Create a register.html file

env > mysite > main > templates > main > (New File) register.html

Django register.htmlGIF

Right click on the templates main folder and click New File to create a file named register.html.

 

Code-it-yourself: Add Django template language to register.html

env > mysite > main > templates > main > register.html

{% extends 'main/header.html' %}

	{% block content %}

	<!-- Register page HTML will go here-->

	{% endblock %}

Create register.html in the templates > main folder.  Just like before, extend the header.html file so we can have access to the main HTML structural elements and Bootstrap CDN in register.html. Add the extendsblock content, and endblock tags in their correct positions. We'll fill in the rest of the HTML soon.

 

Code-it-yourself: Add register path to urls.py

env > mysite > main > urls.py

from django.urls import path
from . import views       

app_name = "main"   

urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register", views.register, name="register"), #add this
]

Add a register path that connects to a register function in views.py.

 

Add the register function to views.py

env > mysite > main > views.py

...
from .forms import NewUserForm #import NewUserForm from forms.py

# Create your views here.
...

def register(request):         
	form = NewUserForm
	return render (request=request, template_name="main/register.html", context={"form":form})

Let's add the register function to the views.py. First import NewUserForm from .forms at the top of the file.  This is how we pass the form we created from forms.py to views.py. Next we need to pass the form to the register.html template as context. Add a new register function and declare a variable equal to the form we imported. Then return a render of the register.html template and pass the form as context.

 

Add HTML code inside block tags of register.html

env > mysite > main > templates > main > register.html

{% extends "main/header.html" %}

	{% block content %}          

	<!--Register--> 
	<div class="container py-5">
	    <h1 class="font-weight-bold">Register</h1>
	    <hr>
	    <br>
		<form method="POST">
			{% csrf_token %}
			{{ form }}                    
			<br>
			<button class="btn btn-primary" type="submit">Register</button>
		</form>
		<br><br>
		<p>If you already have an account, <a href="/login">login</a> instead.</p>
	</div>

	{% endblock %}

Create a division with container as a class attribute. Next, nest a Register header using the heading three element.  Code a form element with a method="POST" attribute for submission purposes. Next add the {% csrf_token %} to prevent form submission forgery. Since we have passed the NewUserForm as form in the views.py function for this page, we can include the form with DTL by using double brackets: {{form}}Finally, the form will contain an HTML button element to submit the form. After the closing form tag and a few breaks, we'll include a link to a login page that we have not yet created but will add later.

 

Add a link in the navbar to view the register page

env > mysite > main > templates > main > includes > navbar.html

<!--Navbar-->
<nav class="navbar navbar-expand-sm navbar-light bg-light">
 <a class="navbar-brand" href="/">Encore</a>
 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
   <span class="navbar-toggler-icon"></span>
 </button>
 <div class="collapse navbar-collapse" id="navbarText">
   <ul class="navbar-nav mr-auto">
     <li class="nav-item">
       <a class="nav-link" href="/products">Products</a>
     </li>
     <li class="nav-item">
       <a class="nav-link" href=" ">Blog</a>
     </li>
     <li class="nav-item">
       <a class="nav-link" href=" ">Contact</a>
     </li>
   </ul>
   <a class="btn btn-sm btn-primary m-2"  href="/register">Register</a> <!--add this-->
 </div>
</nav>

After the navbar-nav unordered list, add an anchor element with an href attribute that equals /register. Remember, the href attribute links to our urls.py file, which will then link to our register function in views.py.

 

View register page

Register page submission not workingGIF

Click on the Register link in the navbar. This will bring you to the http://127.0.0.1:8000/register page to view the register.html page. However, if we submit information into the form, the page will not work and a new user will not appear in the admin page. Why? Our register function in our views.py file is not configured to accept information. Instead, the page simply reloads. Let's fix that in the next step.








3: Allowing user registration


Since we set our form's method to POST in the previous step, we need to add a condition that checks for POST request methods in the existing register function.  Then, we can save the register form and login the user.

 

Add functionality to the register form

env > mysite > main > views.py

from django.shortcuts import render, redirect #add this
from .models import Product
from django.core.paginator import Paginator
from .forms import NewUserForm
from django.contrib.auth import login  #add this

...

def register(request):
	if request.method == "POST":
		form = NewUserForm(request.POST)
		if form.is_valid():
			user = form.save()
			login(request, user)
			return redirect("main:homepage")
	form = NewUserForm
	return render (request=request, template_name="main/register.html", context={"form":form})

Go to the register function in the views.py file. Make sure to import login and redirect at the top of the file.  Then, add an if condition to the register function check if the request method is a POST, as opposed to a GET request method.

You may have noticed when loading your homepage, a GET request method appears in the Command Prompt/Terminal every time the page is loaded. GET and POST are two common HTTP request methods that signify when a user requests information (GET) or sends information (POST). Since users will submit information to the server to register, we set the method of the request to be POST in the register template.

If the request method is indeed "POST", then we create a form variable that equals a NewUserForm and takes in the information that the user submitted as a parameter.  We check if the form is valid so that the submitted information will work when we attempt to save a user. Next, we save the user, log them in and return a redirect to the homepage so that the user returns to the homepage.

The last two lines of the original register function are still necessary since the user submits a GET request method to initially view the register.html page and the form.  In other words, when a user clicks on the register link in the navbar they will submit a GET request method and when they click on the register button to create an account, they will submit a POST request method.  The register function handles the request methods accordingly.  

 

Register a new user on the register page

Functioning Django register pageGIF

Now that we added the necessary code to register a new user in the views.py file, let's try out the registration page. Go to http://127.0.0.1:8000/register and add a test user by submitting the registration form. Upon submission, your page will redirect to the homepage given the return redirect("main:homepage") in the register function.

Then visit the Django admin at http://127.0.0.1:8000/admin/. You'll need to sign in with your previously created superuser username and password since this test user does not have superuser privileges.  Once logged in to the superuser account in the admin, click "Users" to check if the test user was successfully added.






Quiz Questions


1. Why does the navbar.html template not include block tags?


2. What is the difference between the HTTP request methods GET and POST?


Next lesson


Check out the comments and debug buttons if you get stuck.