Django for Beginners #1 - Getting Started
Tags: django, python, webdev, beginners
Published: 2023-03-28 14:20:00

> [Download source code here.](https://www.ericsdevblog.com/posts/django-for-beginners-1?ref=Dev_Community/) ⬅️

Django is a high-level, free and open-source web framework written in Python. It is widely used for building complex web applications and is known for its ability to handle high traffic, its security features and for its reusable components. It follows the model-view-controller (MVC) architectural pattern and comes with a built-in admin interface, authentication support and an ORM (Object-Relational Mapper) that allows you to define your database schema using Python classes.

In addition, Django has a large and active community that provides a wealth of tutorials, packages, and other resources for developers to use. Many well-known and high-traffic websites, such as Instagram, Pinterest and The Washington Times, are built using Django. It is considered one of the most popular and powerful web frameworks for Python, it is widely used by both start-ups and large companies, making it a great choice for building robust and scalable web applications. Its flexibility and scalability make it a top choice for developers and organizations looking to build complex web applications with a minimal footprint.

## Installing necessary tools

To get started, there are a few tools that you need to install on your machine. For a web application, you need at least a programming language (Python), a database (SQLite), and a server (Django's built-in dev server) for the application to run successfully. Remember that this stack is only for the dev environment, so you should never use it in the [production environment](https://www.ericsdevblog.com/posts/how-to-deploy-a-django-project?ref=Dev_Community/).

And, of course, you also need an IDE, such as PyCharm, or at least a code editor. I'll use VS Code for this tutorial since it is a free software.

- [Download Python](https://www.python.org/downloads/)
- [Download SQLite](https://www.sqlite.org/index.html)
- [Download VS Code](https://code.visualstudio.com/download)
- [Download PyCharm](https://www.jetbrains.com/pycharm/download/#section=mac)

## Creating a new Django project

After everything has been properly installed, it is time to start coding! First of all, you need to create a new working directory to keep everything organized:

```cmd
mkdir django-demo
```

```cmd
cd django-demo
```

Next, create a new [Python virtual environment](https://docs.python.org/3/tutorial/venv.html) inside the working directory. This virtual environment will be isolated from your system's Python environment, as well as other Python projects you might have on your machine.

```cmd
python -m venv env
```

Please note here if you are using Linux or macOS, you might have to run `python3` instead of `python`, but I'll keep using `python` for simplicity.

This command will create an `env` directory containing the virtual environment you just created. You can activate the virtual environment using the following command:

```cmd
source env/bin/activate
```

If you are using Windows, use this command instead:

```cmd
env/Scripts/activate
```

If the virtual environment has been successfully activated, your terminal prompt will look like this:

```text
(env) eric@djangoDemo:~/django-demo$
```

`(env)` means you are currently working inside a virtual environment named `env`.

Now it is time for us to initialize a new Django project. You need to install the `Django` package by running the following command:

```cmd
python -m pip install Django
```

Next, you can use the `django-admin` command to create a new Django project:

```cmd
django-admin startproject djangoBlog
```

A new `djangoBlog` directory will be created:

![django new project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8dyco55ud7u4fi2ugyw9.png)

I prefer to restructure the project a bit so that everything starts with the project root directory. You don't have to do this if you don't want to.

![django new project restructure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58rl020v0arw79tl939v.png)

### Create a blog app

Right now, the project is still empty, and as you can see, the project structure is very simple compared to Laravel. We'll discuss each file in detail 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, and videos, or they could be completely independent of each other. It entirely depends on your own need.

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

```cmd
python manage.py startapp blog
```

You should see a new `blog` folder created under the project root directory. Here is a small command-line trick, you can list the content of a directory using the following command:

```cmd
ls
```

If you want to include hidden files as well:

```cmd
ls -a
```

If you want to see the file structure:

```cmd
tree
```

You might have to install [`tree`](https://www.geeksforgeeks.org/tree-command-unixlinux/) for this to work, depending on your operating system.

From now on, I'll use this command to display the file structure. Right now, your project should have the following structure:

```text
.
├── blog
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── djangoBlog
│   ├── asgi.py
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── env
│   ├── bin
│   ├── include
│   ├── lib
│   ├── lib64 -> lib
│   └── pyvenv.cfg
└── manage.py
```

Next, you need to register this new `blog` app with Django. Go to `settings.py` and find `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    'blog',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

### Start the server

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

```cmd
python manage.py runserver
```

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

```text
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 19, 2022 - 01:39:33
Django version 4.1.2, using settings 'djangoBlog.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/](http://127.0.0.1:8000/)

![django welcome page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r91cwq8l45w9s2lkfwio.png)

## Application Structure

Before we can start coding, let's talk about the structure of this new Django application and what each file does.

```text
.
├── blog
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── djangoBlog
│   ├── asgi.py
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── env
│   ├── bin
│   ├── include
│   ├── lib
│   ├── lib64 -> lib
│   └── pyvenv.cfg
└── manage.py
```

### The root directory

- `manage.py`: A command-line utility that lets you interact with this Django project in various ways.
- `djangoBlog`: The main project directory, which contains the settings file and the entry point of the project.
- `blog`: The `blog` app.

### The project directory

- `djangoBlog/__init__.py`: An empty file that tells Python that this directory should be considered a Python package.
- `djangoBlog/settings.py`: As the name suggests, it is the setting/configuration file for this Django project.
- `djangoBlog/urls.py`: The URL declarations for this Django project, a _table of contents_ of your Django application. We'll talk more about this later.
- `djangoBlog/asgi.py`: An entry-point for ASGI-compatible web servers to serve your project.
- `djangoBlog/wsgi.py`: An entry-point for WSGI-compatible web servers to serve your project.

### The app directory

- `blog/migrations`: This directory contains all the migration files for the blog app. Unlike Laravel, these files are automatically generated by Django based on your models.
- `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 logic of this app.

## Configuring our Django project

Before we dive into our project, there are some changes you 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 database settings that our website 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 demo project, but it won't work for large sites. So, if you want to use other databases, here is an example:

```python
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "<database_name>",
        "USER": "<database_user_name>",
        "PASSWORD": "<database_user_password>",
        "HOST": "127.0.0.1",
        "PORT": "5432",
    }
}
```

As a side note, Django recommends using PostgreSQL as your database in a production environment. I know many tutorials are teaching you how to use Django with MongoDB, but that's actually a bad idea. MongoDB is a great database solution, it just doesn't work so well with Django. You can read [this article](https://daniel.feldroy.com/posts/when-to-use-mongodb-with-django) 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. We'll create a `static` directory inside the blog app for our Django site. This is where we store static files for the `blog` app during development.

```text
blog
├── admin.py
├── apps.py
├── __init__.py
├── migrations
├── models.py
├── static
├── tests.py
└── views.py
```

In the **production** environment, however, things are a bit different. You need a different folder under the root directory of our project. Let's call it `staticfiles`.

```text
.
├── blog
├── db.sqlite3
├── djangoBlog
├── env
├── manage.py
└── staticfiles
```

Then you need to specify this folder in the `settings.py`.

```python
STATIC_ROOT = "staticfiles/"
```

Next, you 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 later.

```python
STATIC_URL = "static/"
```

**Media Files**

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

```text
.
├── blog
├── db.sqlite3
├── djangoBlog
├── env
├── manage.py
├── mediafiles
└── staticfiles
```

And then, specify the location and URL in the `settings.py` file:

```python
# Media files

MEDIA_ROOT = "mediafiles/"

MEDIA_URL = "media/"
```

## URL dispatchers in Django

In the web development field, there is something we call the MVC (Model-View-Controller) structure. In this structure, the model is in charge of interacting with our database, each model should correspond to one database table. The view is the frontend part of the application, it is what the users can see. And finally, the controller is the backend logic of the app, such as retrieving data from the database through the models, putting them in the corresponding view, and eventually returning the rendered template back to the user.

Django is a web framework designed based on this structure, just with different terminology. For Django, it is the MTV (Model-Template-View) structure. The template is the frontend, and the view is the backend logic.

In this tutorial, our focus will be 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 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`:

```text
djangoBlog
├── asgi.py
├── __init__.py
├── __pycache__
├── settings.py
├── urls.py
└── wsgi.py
```

The file gives us an example of what an URL dispatcher should look like:

```python
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]
```

It reads information from a URL and returns a view. If you've followed the tutorial series on [Laravel](https://www.ericsdevblog.com/posts/laravel-for-beginners-1?ref=Dev_Community/), do not confuse this view with the view in Laravel. Remember that Django's view is, in fact, Laravel's controller.

In this example, if the domain is followed by `admin/`, the dispatcher will route the user to the admin page.

### Passing parameters with Django URL dispatchers

It is often necessary to take URL segments and pass them to the view as extra parameters. For example, if 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 searching for. This is how we can pass a parameter:

```python
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, and it captures an integer parameter. Any string, excluding a `/` character, is matched if a converter isn't included. 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`](https://docs.python.org/3/library/uuid.html#uuid.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 patterns

Sometimes the pattern you need to match is more complicated, in which case you can use regular expressions to match URL patterns. We briefly talked about regular expressions in our [JavaScript course](https://www.ericsdevblog.com/posts/javascript-basics-1?ref=Dev_Community/).

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

```python
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 multiple different apps in it. If you put all URL configurations in one file, it will be really messy. When this happens, you can separate the URL configurations and move them to different apps. For example, in our project, we can create a new `urls.py` inside the `blog` app.

```python
from django.urls import path, include

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

This means if the URL has the pattern `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:

```python
from django.urls import path
from blog import views

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

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

### Naming URL patterns

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

```python
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 cover for the basics of URL dispatcher. However, you may have noticed that something is missing. What if the HTTP requests have different methods? How could we differentiate them? In Django, you cannot match different HTTP methods inside the URL dispatcher. All methods match the same URL pattern, and we'll have to use flow control to write different codes for different methods inside the view functions. We'll discuss this in detail in future articles.

> If you liked this article, please also check out my other tutorials:
> - [Laravel for Beginners](https://www.ericsdevblog.com/posts/laravel-for-beginners-1?ref=Dev_Community/)
> - [Vue.js for Beginners](https://www.ericsdevblog.com/posts/vuejs-for-beginners-1?ref=Dev_Community/)
> - [Create a Modern Application with Django and Vue](https://www.ericsdevblog.com/posts/create-a-modern-application-with-django-and-vue-1?ref=Dev_Community/)
> - [Beginner’s Roadmap to Web Development](https://www.ericsdevblog.com/series/beginners-roadmap-to-web-development?ref=Dev_Community/)