Getting started with Django 3.0 2020

django Apr 23, 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!

We started sharing these tutorials and articles to help and inspire developers and engineers around the world. If our blog has been helpful to you, feel free to buy us a coffee to keep us going :).

Buy us a coffeeBuy us a coffee

Ousseynou Diop

Full stack Developer & Python Lover

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.