Getting started with Django 3.0 | 2020
In this tutorial i will share with you how to build real world application with Django.
What is Django ?
Django is a high level web framework built in top of Python.
Who use Django ?
Django is used by lot of companies such as Facebook, Google, Pinterest, Mozila etc...
What we will build ?
We go to build a simple phone-book application, but the ideas in this tutorial is applicable in any application.
Let's getting started :
Django installation
To install django in our machine we need to follow theses steps :
- Install Python with PIP
- Create a virtual environment
- Install Django on it
Follow these instructions to install Python and PIP. I will create a folder for this project and create an envirotnemnet on it :
$ mkdir django-3-0
$ cd django-3-0
$ python3 -m venv ./my-env
After that we need to activate it
$ source my-env/bin/activate
(my-env)
We will use Django CLI, it comes with very useful commands.
(my-env) $ django-admin startproject phonebook && cd phonebook
To to our application we need to run django server
(my-env) $ python manage.py runserver
The server will start on http://localhost:8000/ Open your browser and go to http://localhost:8000/
Let’s have a look at what startproject created:
├── manage.py
├── phonebook
│ ├── asgi.py
│ ├── **init**.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
└── Pipfile.lock
These files are:
- The outer phonebook/ root directory is just a container for your project. Its name doesn’t matter to Django; you can rename it to anything you like.
- manage.py: A command-line utility that lets you interact with this Django project in various ways. You can read all the details about manage.py in django-admin and manage.py.
- The inner phonebook/ directory is the actual Python package for your project. Its name is the Python package name you’ll need to use to import anything inside it (e.g. phonebook.urls).
- phonebook/init.py: An empty file that tells Python that this directory should be considered a Python package. If you’re a Python beginner, read more about packages in the official Python docs.
- phonebook/settings.py: Settings/configuration for this Django project. Django settings will tell you all about how settings work.
- phonebook/urls.py: The URL declarations for this Django project; a “table of contents” of your Django-powered site. You can read more about URLs in URL dispatcher.
- phonebook/wsgi.py: An entry-point for WSGI-compatible web servers to serve your project. See How to deploy with WSGI for more details.
Contact application
In order to create our first application we need to run :
(my-env) $ python manage.py startapp contact
Our application looks like this
├── contact
│ ├── admin.py
│ ├── apps.py
│ ├── **init**.py
│ ├── migrations
│ │ └── **init**.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── db.sqlite3
├── manage.py
├── phonebook
│ ├── asgi.py
│ ├── **init**.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
└── Pipfile.lock
we will explain later the details of files . Don't forget to add contact to your settings :
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'contact' # add this
]
Now inside contact folder let's create our database schema, every contact we need to save :
-First Name -Last Name -Email -Phone Number -The use who created it -And the timestamp
Open models.py and write :
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Contact(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
phone = models.CharField(max_length=150)
email = models.CharField(max_length=150, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.phone
Here we import the User table from django.contrib and create a Python class, this represent our database schema. By convention we use underscore (_) to separate words. Before going any further we need to migrate our database. In your terminal stop the server and run :
(my-env) $ python manage.py makemigrations
Migrations for 'contact':
contact/migrations/0001_initial.py
- Create model Contact
(my-env) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contact, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying contact.0001_initial... OK
Applying sessions.0001_initial... OK
Let's create a superuser:
(my-env) $ python manage.py createsuperuser
Username (leave blank to use 'hackerpro'): admin
Email address: admin@gmail.com
Password:
Password (again):
Superuser created successfully.
Open your browser and go to http://localhost:8000/admin What you see ? Now we need to add our contact application to the admin. Open admin.py file and write :
# admin.py
from django.contrib import admin
from .models import Contact
# Register your models here.
admin.site.register(Contact)
# views.py
from django.shortcuts import render
def index(request):
return render(request, "index.html")
Write this in views.py this is a function and it return a template called index.html
We need to define our urls to map this views. Let's create a urls.py file in your main urls.py file add this :
from django.contrib import admin
from django.urls import path, include # add this
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("contact.urls")) # add this
]
Now our application looks like this :
├── contact
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py #new
│ └── views.py
├── db.sqlite3
├── manage.py
├── phonebook
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
└── Pipfile.lock
In contact folder go to urls.py and put :
from django.urls import path
from .views import index
urlpatterns = [
path("", index, name="home")
]
Open your browser and referech the page, what you see ? You see an error like this TemplateDoesNotExist at / To solve this add this code in your settings.py
ROOT_URLCONF = 'phonebook.urls'
TEMPLATE_DIR = os.path.join(BASE_DIR, "templates") # add this
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR], # add this
'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',
],
},
},
]
Now we've this structure :
├── contact
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── db.sqlite3
├── manage.py
├── phonebook
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
├── Pipfile.lock
└── templates # new
└── index.html # new
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Home page</title>
</head>
<body>
<h2>Home page</h2>
</body>
</html>
Display the list of contacts
open views.py and put :
from django.shortcuts import render
from .models import Contact # add this
def index(request):
return render(request, "index.html")
# add this
def contact_list(request):
contacts = Contact.objects.all()
return render(request, "contact_list.html", {"contacts": contacts})
and in urls.py
from django.urls import path
from .views import index, contact_list # add this
urlpatterns = [
path("", index, name="home"),
path("contacts/", contact_list, name="contacts") # add this
]
In our template contact_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Contacts</title>
</head>
<body>
<h2>Contacts</h2>
<!-- Check if we've contacts -->
{% if contacts %}
<!-- iterate over contact -->
{% for contact in contacts %}
<p>
{{ contact.first_name }}
</p>
{% endfor %}
{% else %}
<p>No contact</p>
{% endif %}
</body>
</html>
What ? Why you repeat your self ? Do you know DRY concept ? in order to not repeat our self we need to create a base template : in /templates create a file and call it base.html I use Bootstrap starter template
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<!-- Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<title>Phonebook!</title>
</head>
<body>
<h2 class="text-center">
{% block page_title %}
{% endblock %}
</h2>
<div class="container">
{% block content %}
{% endblock %}
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script
src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"
></script>
</body>
</html>
Change the index.html and contact_list.html
{% extends 'base.html' %}
{% block page_title %}
Home page
{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
Home page
</div>
</div>
{% endblock %}
{% extends 'base.html' %}
{% block page_title %}
Contacts
{% endblock %}
<!-- Check if we've contacts -->
{% if contacts %}
<!-- iterate over contact -->
{% for contact in contacts %}
<p>
{{ contact.first_name }}
</p>
{% endfor %}
{% else %}
<p>No contact</p>
{% endif %}
Read more about Django template syntax here!
In this tutorial we will finish our application, at the end of this tutorial you'll have a fully functional Django application. We will implement :
- Forms(Login and registration)
- Template
- Full CRUD functionalities
Let's write our first form, to do so we need to create a new application and called it accounts
Run
(my-env) $ python manage.py startapp accounts
Inside accounts app create a file and call it forms.py
Now we've this tree of files :
├── accounts # new
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py # new
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── contact
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── db.sqlite3
├── manage.py
├── phonebook
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
├── Pipfile.lock
└── templates
├── base.html
├── contact
│ ├── contact_details.html
│ ├── contact_list.html
in accounts/forms.py add
# forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class LoginForm(forms.Form):
username = forms.CharField(
label="Username",
widget=forms.TextInput(
attrs={
"class": "form-control"
}
))
password = forms.CharField(
label="password",
widget=forms.PasswordInput(
attrs={
"class": "form-control"
}
))
class SignUpForm(UserCreationForm):
username = forms.CharField(
label="Username",
widget=forms.TextInput(
attrs={
"class": "form-control"
}
))
email = forms.EmailField(
label="Email ",
widget=forms.EmailInput(
attrs={
"class": "form-control"
}
))
password1 = forms.CharField(
label="password",
widget=forms.PasswordInput(
attrs={
"class": "form-control"
}
))
password2 = forms.CharField(
label="re-enter your password",
widget=forms.PasswordInput(
attrs={
"class": "form-control"
}
))
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2')
in accounts/views.py add
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from .forms import LoginForm, SignUpForm
def login_view(request):
form = LoginForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return redirect("/contacts/")
return render(request, "accounts/login.html", {"form": form})
def register_user(request):
if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get("username")
raw_password = form.cleaned_data.get("password1")
user = authenticate(username=username, password=raw_password)
return redirect("/login/")
else:
form = SignUpForm()
return render(request, "accounts/register.html", {"form": form})
Create urls.py and add
from django.urls import path
from .views import login_view, register_user
from django.contrib.auth.views import LogoutView
urlpatterns = [
path('login/', login_view, name="login"),
path('register/', register_user, name="register"),
path("logout/", LogoutView.as_view(), name="logout")
]
Don't forget to update your main urls.py
from django.contrib import admin
from django.urls import path, include # add this
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("contact.urls")), # add this
path("", include("accounts.urls")) # add this
]
Now you understand how Django works, here is the full application on Github
Read more about Django Here!
Thanks for reading!