How to Create -> Registration + Login Web App with Python & Django (works for various versions)
Why ?
I have been using Python (along with bash et al) for a long time now for my Automation and Web Crawling & Scraping endeavors, I was confused between opting for Flask or extensive Django for a mock Web App but decided to use the more robust Django used for back-end programming in Python due to it’s wider functionality compared to the simplistic Flask. It seemed like a perfect fit for a mini hobby project. I used Ubuntu for the Whole project and the same has worked perfectly in Windows too, but the process itself is essentially the same for any Operating System since Python and Django both are cross-platform compatible. Just follow the install instructions carefully, advanced users could even try some compilation. What Django uses is the Model-Template-View (MTV) pattern which is quite similar to the widely used MVC pattern of making Web Apps. While many would argue that Python is not a good fit for Web Development and I agree with them, it’s better to learn JavaScript if you are looking for a future as a Web Developer, but as an experiment or for those who work closely with Python, Django and Flask are a boon as they are both simple and highly functional even for production quality apps.
Where ?
Start with downloading and installing Python : https://www.python.org/downloads/ (and we will get Django package later on)
A Django Project consists of four major Python files within the project tree namely settings.py
, views.py
, urls.py
, models.py
The settings file deals with all the Django project settings at system level , the urls file stores all the URL patterns for our project that stem out and redirect from our main page. We store our data models in Python Django format in the models file which is then translated into SQLite operations by default in the process without any explicit need to code. Finally the views file is where the request and response functions are handled and the page serving is done. For other files and their usage go ahead and read the Django documentation and its glossary, as any and every good developer would say : RTFM (Read the F*&^ing Manual) :
https://docs.djangoproject.com/en/2.1/glossary/ : old
https://django.readthedocs.io/en/latest/intro/overview.html : latest
Optionally check out the Github repository located at https://github.com/django/django
How ?
The whole process from scratch to creating a Registration & Login page in short can be broken down into three simple steps :
1 > Creating the Project structure
Be sure to include Python executive in your PATH environment variable and check if pip command works on your terminal. Also substitute Python with whatever executive call works in the terminal such as Py or Python3 etc in case calling Python has no effect.We make use of something called a virtual environment to make our project locally sustainable without bloating the main Python installation and encapsulate it.Since `virtualenv` was deprecated I removed the lines present here on it, we will be making use of `venv` : https://docs.python.org/3/library/venv.htmlWe make a project folder and change our current directory to it by using$ mkdir djangox$ cd djangoxNext we set up a virtual environment (a mini python package store if you may) named `denvx` by executing$ python -m venv denvxalso activate (source) denvx$ source /denvx/bin/activateFor installing Django framework into our virtual environment(denvx) use pip (pip is the python package manager it comes installed by default in most cases)$ pip install djangoWe now start our django-project using$ django-admin startproject dprojxalso make an django App for Registration and Login page with$ cd dprojx && django-admin startapp dappxNext we create additional folders such as '/templates', '/media' and '/static' ... for our resources$ mkdir templates media static media/profile_pics templates/dappxDjango now has models and views created for our app 'dappx' which should me migrated for which we use$ python manage.py migrate
$ python manage.py makemigrations dappx
$ python manage.py migrateThis migrates the changes for our created project.
Your Project Structure should now look something like this :
2 > Modifying the Project Files
Open up dprojx/settings.py and add the lines for other _DIR to look like :
# dprojx/settings.py# Build paths inside the project like this: os.path.join(BASE_DIR, …)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATE_DIR = os.path.join(BASE_DIR,'templates')
STATIC_DIR = os.path.join(BASE_DIR,'static')
MEDIA_DIR = os.path.join(BASE_DIR,'media')
Also in the installed apps section you need to add dappx for it to be recognized and registered :
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'dappx',
]
Add TEMPLATE_DIR to ‘DIRS’: [] section too :
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR,],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Add these lines at the end of our dprojx/settings.py for static, templates and media folder :
STATIC_URL = '/static/'
STATICFILES_DIRS = [STATIC_DIR,]MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = ‘/media/’LOGIN_URL = ‘/dappx/user_login/’
Next we create the dappx/models.py to be used that will be the basis of our forms.py :
# dappx/models.pyfrom django.db import models
from django.contrib.auth.models import User# Create your models here.class UserProfileInfo(models.Model):user = models.OneToOneField(User,on_delete=models.CASCADE)portfolio_site = models.URLField(blank=True)profile_pic = models.ImageField(upload_to='profile_pics',blank=True)def __str__(self):
return self.user.username
from models.py we now switch to dappx/forms.py :
# dappx/forms.pyfrom django import forms
from dappx.models import UserProfileInfo
from django.contrib.auth.models import Userclass UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username','password','email')class UserProfileInfoForm(forms.ModelForm):
class Meta():
model = UserProfileInfo
fields = ('portfolio_site','profile_pic')
Register your models in dappx/admin.py too :
# dappx/admin.pyfrom django.contrib import admin
from dappx.models import UserProfileInfo, User
# Register your models here.admin.site.register(UserProfileInfo)
The most important bit comes into play now where our View is generated for our .html pages in dappx/views.py :
# dappx/views.pyfrom django.shortcuts import render
from dappx.forms import UserForm,UserProfileInfoForm
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from django.contrib.auth.decorators import login_requireddef index(request):
return render(request,'dappx/index.html')@login_required
def special(request):
return HttpResponse("You are logged in !")@login_required
def user_logout(request):
logout(request)
return HttpResponseRedirect(reverse('index'))def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
profile_form = UserProfileInfoForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'profile_pic' in request.FILES:
print('found it')
profile.profile_pic = request.FILES['profile_pic']
profile.save()
registered = True
else:
print(user_form.errors,profile_form.errors)
else:
user_form = UserForm()
profile_form = UserProfileInfoForm()
return render(request,'dappx/registration.html',
{'user_form':user_form,
'profile_form':profile_form,
'registered':registered})def user_login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request,user)
return HttpResponseRedirect(reverse('index'))
else:
return HttpResponse("Your account was inactive.")
else:
print("Someone tried to login and failed.")
print("They used username: {} and password: {}".format(username,password))
return HttpResponse("Invalid login details given")
else:
return render(request, 'dappx/login.html', {})
The templates can be arranged now for generating the views, we use four templates for this : ‘base.html’, ‘registration.html’, ‘login.html’, ‘index.html’ :
base.html :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Base</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></head>
<body>
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<ul class="nav navbar-nav">{# Django Home Link / Admin Link / Register Link#}
<li><a class="navbar-brand" href="{% url 'index' %}">DJANGO</a></li>
<li><a class="navbar-link" href="{% url 'admin:index' %}">Admin</a></li>
<li><a class="navbar-link" href="{% url 'dappx:register' %}">Register</a></li>
{# Some logic on what to display for last item#}
{% if user.is_authenticated %}
<li><a href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li><a class="navbar-link" href="{% url 'dappx:user_login' %}">Login</a></li>
{% endif %}</ul>
</div>
</nav>
<div class="container">
{% block body_block %}
{% endblock %}
</div>
</body>
</html>
index.html :
{% extends "dappx/base.html" %}
{% block body_block %}<div class="container">
<div class="jumbotron">
<h1>Welcome to the Djungle !</h1>
{% if user.is_authenticated %}
<h2>Hello {{ user.username }}</h2>
{% else %}
<h2>Register or Login if you'd like to</h2>
{% endif %}
</div>
</div>{% endblock %}
login.html :
{% extends 'dappx/base.html' %}
{% block body_block %}
<div class="container">
<div class="jumbotron">
<h1>Login here :</h1><form method="post" action="{% url 'dappx:user_login' %}">
{% csrf_token %}
{# A more "HTML" way of creating the login form#}
<label for="username">Username:</label>
<input type="text" name="username" placeholder="Username"><label for="password">Password:</label>
<input type="password" name="password" placeholder="Password"><input type="submit" name="" value="Login"></form></div>
</div>
{% endblock %}
registration.html :
{% extends "dappx/base.html" %}
{% load static %}{% block body_block %}
<div class="container">
<div class="jumbotron">
{% if registered %}
<h1>Thank you for registering!</h1>
{% else %}
<h1>Register Here</h1>
<h3>Just fill out the form.</h3><form enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" name="" value="Register">
</form>
{% endif %}
</div>
</div>
{% endblock %}
We then register the above urls into our project urls.py files for this create a file dappx/urls.py :
# dappx/urls.pyfrom django.conf.urls import url
from dappx import views# SET THE NAMESPACE!
app_name = 'dappx'# Be careful setting the name to just /login use userlogin instead!
urlpatterns=[
url(r'^register/$',views.register,name='register'),
url(r'^user_login/$',views.user_login,name='user_login'),
]
In the main urls.py file the rest of the pattern is specified :
# dprojx/urls.pyfrom django.contrib import admin
from django.urls import path
from django.conf.urls import url,include
from dappx import viewsurlpatterns = [
path('admin/', admin.site.urls),
url(r'^$',views.index,name='index'),
url(r'^special/',views.special,name='special'),
url(r'^dappx/',include('dappx.urls')),
url(r'^logout/$', views.user_logout, name='logout'),
]
By now your project structure should look somewhat like :
Again apply the migrations using :
$ python manage.py migrate
$ python manage.py makemigrations dappx
$ python manage.py migrate
and create a superuser (an admin for the project basically) using the command :
$ python manage.py createsuperuser
Enter your desired username, email and password.
3 > Firing Up the Server !
Now you are ready to start your server :
$ python manage.py runserverPerforming system checks...System check identified no issues (0 silenced).
July 18, 2018 - 04:03:03
Django version 2.0.7, using settings 'dprojx.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
There we have it
Navigate to http://127.0.0.1:8000/ to see your Django powered site in action.
Here are the Login and Registration pages :
Edit (01|04|19) :
For some of you who has been asking about an error like ModelForm has no Model specified.
That error was due to a indentation error while copy-pasting the code to your editor and can be solved by going into the indentation settings and replacing all Tabs to four spaces or vice-versa. Also remember Django and Python is case-sensitive so Model
and model
are different. If the error persists try to look for indentation error in the specific file and line and correct it manually as YooYoung Ko pointed out. Cheers
Edit (09|04|20) :
I had time to check this article out due to the Corona pandemic going on and me randomly checking old stuff, hope you all and your families are safe, thanks to George Ofonedu this guide is compatible with Django 3.0 with minor changes that is `staticfiles` being renamed to `static` in Django templates with version `3.0` and I have edited it to reflect that change. Also many many thanks for the 1k+ claps I never expected this write up to go that far since it was just a hobby project I did.
Edit (22|06|21) :
The Corona Pandemic is still raging on, hope everyone is safe, my family just recovered from the disease so I know how tough it can be. I have tested the code to be compatible since I had some free time. Point out if there are any errors., do check out my other articles too if you found this useful.
Edit (03|06|24) :
This page is quite useful if you want to learn how to make parts of the code and process reusable : https://django.readthedocs.io/en/stable/intro/reusable-apps.html