Django for Beginners #1

You can download the source code for this tutorial here.

Django is a Python-based web framework that allows you to quickly create a web application in a matter of hours. In this course, we are going to investigate the basics of Django by creating a blog application.

Compared to Laravel, Django has both some advantages and disadvantages. For instance, Django is based on Python, a very popular programming language, which means there are a lot of different packages that perform all kinds of different tasks, and unlike Laravel, Django has a very powerful built-in admin panel, which we’ll discuss in detail later.

The disadvantage is that Python’s popularity in the field of web development only started recently, which means most server control panels aren’t designed for Django projects. As beginners, we could run into a lot more trouble when we deploy our app.

Installing necessary tools

Before we start building our app, there are a few tools that you need to install on your machine. As you know, for a web application, we need a programming language (Python), a database (SQLite) and a server (Django’s built-in dev server). Remember that this stack is only for the dev environment, you should never use it in a real production environment.

And of course, we also need an IDE, such as PyCharm, or at least a code editor. For this course, I’ll go with VS Code, since it is completely free to use.

Download Python

Download PyCharm

Download VS Code

Creating a new Django project

To create a new Django project, we need to first install the Django package. Run the following command:

1
python -m pip install Django

Depending on the system you are using, it is possible that your OS uses Python 2 by default. In that case, you may need to change python into python3. The same is true for all the future commands we might use in this course. And if you are using Windows, you may need to use py instead.

Next, we can use the django-admin command to create a new Django project. Go to your work folder and run the following command:

1
django-admin startproject example

Start the server

Now, we can start the development server to test if everything works. Open the terminal, and run the following commands:

1
2
cd example
python manage.py runserver

You’ll see the following output on the command line:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

October 09, 2020 - 15:50:53
Django version 3.1, using settings 'myproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Open the browser and go to http://127.0.0.1:8000/

django welcome page

Create a blog app

Right now, the project is still empty. And as you can see, the file structure is very simple compared to Laravel. We’ll talk about what each of them does later.

Django allows you to create multiple apps in a single project. For example, there could be a blog app, a gallery app, and a forum app inside one single project. These apps could share the same static files (CSS and JavaScript files), images, videos… or they could be completely independent of each other. It completely depends on your own need.

In this tutorial, we’ll create just one blog app. Go back to the terminal, and run this command:

1
python manage.py startapp blog

Again, remember to change python to python3 or py if it doesn’t work on your system.

You should see a new blog folder created inside the project root directory. Now, we need to register this new app with Django. Go to settings.py and find INSTALLED_APPS:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
    'blog',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Application Structure

django app structure

The project root directory

  • manage.py: A command-line utility that lets you interact with this Django project in various ways.

The project main directory

  • example/__init__.py: An empty file that tells Python that this directory should be considered a Python package.
  • example/settings.py: As the name suggests, it is the settings/configuration for this Django project.
  • example/urls.py: The URL declarations for this Django project; a “table of contents” of your Django-powered site. We’ll talk more about this in the next article.
  • example/asgi.py: An entry-point for ASGI-compatible web servers to serve your project.
  • example/wsgi.py: An entry-point for WSGI-compatible web servers to serve your project.

The app directory

  • blog/migrations: This folder contains all the migration files for the blog app. Unlike Laravel, these files are automatically generated by Django.
  • blog/admin.py: Django also comes with an admin panel, and this file contains all the configurations for it.
  • blog/models.py: Models describe the structure and relation of the database. The migration files are generated based on this file.
  • blog/views.py: This is equivalent to the Controllers in Laravel. It contains all the core logics in this app.

Configuring our Django project

Before we dive into our project, there are some changes we need to make to the settings.py file.

Allowed hosts

The ALLOWED_HOSTS is a list of domains that the Django site is allowed to serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations.

However, you might notice that even if the ALLOWED_HOSTS is currently empty, we can still access our site using the host 127.0.0.1. That is because when DEBUG is True and ALLOWED_HOSTS is empty, the host is validated against ['.localhost', '127.0.0.1', '[::1]'].

Database

DATABASES is a dictionary containing the settings for the database that our site needs to use. By default, Django uses SQLite, which is a very light weighted database consisting of only one file. It should be enough for our small project, but it won’t work for large sites. So, if you want to use other databases, here is an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

As a side note, Django recommends using PostgreSQL as your database in a production environment. I know there are lots of tutorials teaching you how to use Django with MongoDB, but that’s actually a bad idea. MongoDB itself is a great database solution, it just doesn’t work so well with Django. Read this article for details.

Static and media files

And finally, we need to take care of the static and media files. Static files are CSS and JavaScript files, and media files are images, videos and other things the user might upload.

Static Files

First, we need to specify where these files are stored. For our Django site, we’ll create a static folder inside the blog app. This is where we store static files for the blog app during development.

static files

In the production environment, however, things are a little different. We need a different folder under the root directory of our project. Let’s call it staticfiles.

static files in production environment

Then we specify this folder in the settings.py.

1
STATIC_ROOT = 'staticfiles/'

Next, we need to tell Django what URL to use when accessing these files in the browser. It does not have to be /static, but do make sure it does not overlap with our URL configurations which we’ll talk about in the next article.

1
STATIC_URL = '/static/'

Media Files

Media files are configured in the same way. We create a mediafiles folder in the project root directory:

media files

And then, we specify the location and URL:

1
2
MEDIA_ROOT = 'mediafiles/'
MEDIA_URL = '/media/'

URL dispatchers in Django

Django is a web framework that follows what we call the MTV (Model, Template, View) architecture. In this architecture, the model is in charge of interacting with our database, each model should correspond to one database table. The template is the frontend part of the application, it is what the users are going to see. And finally, the view is the backend logic of the app, it is usually in charge of retrieving data from the database through the models, putting them in the corresponding template, and eventually returning the rendered template back to the user.

In this tutorial, our focus will be on understanding each of these layers, but first, we need to start with the entry point of every web application, the URL dispatcher. What it does is that when the user types in a URL and hits Enter, The dispatcher reads that URL and directs the user to the correct page.

Basic URL configurations

The URL configurations are stored in example/urls.py, the most basic URL configuration looks like this:

1
2
3
4
5
6
7
from django.urls import path

from blog import views

urlpatterns = [
    path('', views.home),
]

It reads information from a URL and returns a view. If you’ve followed my course on Laravel, do not confuse this view with the view in Laravel. Django’s view is in fact Laravel’s controller.

In this example, if the domain is not followed by any argument (http://www.mydomain.com/), Django will return the home view.

Passing parameters with Django URL dispatchers

It is often necessary to take segments of the URL and pass them to the view as extra parameters. For example, you want to display a blog post page, you’ll need to pass the post id or the slug to the view so that you can use this extra information to find the blog post you are looking for. This is how we can pass a parameter:

1
2
3
4
5
6
7
from django.urls import path

from blog import views

urlpatterns = [
    path('post/<int:id>', views.post),
]

The angle brackets will capture a part of the URL as a parameter. On the left side, the int is called a path converter. It captures an integer parameter. If a converter isn’t included, any string, excluding a / character, is matched. On the right side, it is the name of the parameter. We’ll need to use it in the view.

The following path converters are available by default:

  • str - Matches any non-empty string, excluding the path separator, '/'. This is the default if a converter isn’t included in the expression.
  • int - Matches zero or any positive integer. Returns an int.
  • slug - Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters. For example, building-your-1st-django-site.
  • uuid - Matches a formatted UUID. To prevent multiple URLs from mapping to the same page, dashes must be included and letters must be lowercase. For example, 075194d3-6885-417e-a8a8-6c931e272f00. Returns a UUID instance.
  • path - Matches any non-empty string, including the path separator, '/'. This allows you to match against a complete URL path rather than a segment of a URL path as with str.

Using regular expressions to match URL patters

If this type of capturing parameters is too simple for you, and you need something more powerful, you can use regular expressions to too match patterns as well. We briefly talked about regular expressions in our JavaScript course.

To use regular expressions, we need to use re_path() instead of path():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django.urls import path, re_path

from . import views

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
]

Importing other URL configurations

Imagine you have a Django project with 10 apps in it, if you put all URL configurations in one file, it will be really messy. When this happens, we can separate the URL configurations into different apps. For example, in our project, we can create a new urls.py inside the blog app.

1
2
3
4
5
from django.urls import path, include

urlpatterns = [
    path('blog/', include('blog.urls'))
]

This means if the URL is in the form of http://www.mydomain.com/blog/xxxx, Django will go to blog/urls.py and match for the rest of the URL.

Define the URL patterns for the blog app in the exact same way:

1
2
3
4
5
6
7
from django.urls import path

from blog import views

urlpatterns = [
    path('post/<int:id>', views.post),
]

This pattern will match the URL in the form of: http://www.mydomain.com/blog/post/123

Naming URL patterns

One last thing I’d like to mention is that we can name our URL pattern by giving it a third parameter like this:

1
2
3
4
5
urlpatterns = [
    #...
    path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
    #...
]

This little trick is very important, it allows us to reverse resolute URLs from the template. We’ll talk about this when we get to the template layer.

That is all we need to convert for the basics of URL dispatcher. If you’ve studied Laravel, you may have noticed that something seems missing. What if the HTTP request have different methods? How could we differentiate them?

This is the part that I personally think Laravel is doing better than Django. In Django, you cannot match different HTTP methods. All different methods match the same URL pattern, and we’ll have to use flow control to write different codes for different methods. We’ll discuss this in detail in the future.


comments powered by Disqus