In the previous part of the series we created the Portfolio application. ln this part we’ll create a basic structure of the project, its skeleton.
Our app is going to consist of a lot of different elements. In particular, we’ll have models, views and templates. We’ll also have settings and paths in their own files. We’re going to take care of them step by step, but in this part we’ll start with something bigger, an application.
The term ‘application’ is ambiguous in the context of Django development. On one hand, we can refer to the entire project as an application. We’ve used it in this sense in the first sentence of this article. In Django, though, an application is also a large section of the website serving one particular purpose, like the main site or a blog, to mention just a few. We’re going to create only one such section, or application, but there could be more. This way of structuring the project allows us to reuse the applications in other projects.
Table of Contents
Files and Folders
Before we create our application, though, let’s have a look at the file hierarchy that was created for us when we created the project. Here it is in Visual Studio Code:
First, we have the portfolio folder. It contains the following files:
The __init__.py file is an empty file. If an empty __init__.py file is added to a directory, the directory is treated as a Python package.
The settings.py file contains all configuration details for our app. This is where we register applications, set the location of static files (like HTML, CSS and JavaScript files, but also images, for example) and configure the database settings.
The urls.py file contains the top-level URL-to-view mappings, which are used for routing.
The wsgi.py file is used for synchronous communication with the server.
The asgi.py file is used for synchronous and asynchronous communication with the server.
Outside the portfolio folder there’s the manage.py file. It’s used to create applications, start the development web server and communicate with databases.
The catalog Application
Now that we’re done exploring the files, we can create and register our application. Let’s name it catalog. To create it, just make sure you’re in the same folder as the manage.py file and run the following command in the terminal:
python manage.py startapp catalog
Here’s what the newly created folder looks like in Visual Studio Code after expanding:
The particular files that were created inside the application are named after the purpose they serve and we’ll be gradually filling them in as we proceed. There is also the empty __init__.py file that was created so that the folder is recognized as a Python package. Finally, there’s the migrations folder, which will be used for handling migrations, so that when we modify our models, the database will be automatically updated.
Next, we have to register our application so that it’s included in the project. To register the application, just open the settings.py file, find the INSTALLED_APPS
list and add it there:
...
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"catalog.apps.CatalogConfig"
]
MIDDLEWARE = [
...
The CatalogConfig object was generated in the catalog/apps.py file:
from django.apps import AppConfig
class CatalogConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "catalog"
With that in place, let’s now configure the database.
The Database
We need a way to store the data that will be presented in the website. A database seems a reasonable solution. We don’t expect to have a lot of data and the data isn’t going to change rapidly, so we’ll stick with the default SQLite database. What’s more, it’s already configured. Just open the settings.py file again and have a look:
]
...
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
...
That’s it. Our database is ready to be used. The next thing we’ll have a look at is the URL mapper.
URL Mapping
Routing in a Django website may be handled in one central location or application-wise. In the project folder you can see the /portfolio/urls.py file. If you open it, you’ll see the following code:
"""
URL configuration for portfolio project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path("admin/", admin.site.urls),
]
In the instruction you can read about the urlpatterns
list and see some examples. In general, URLs are routed to views, and views are what you see in your browser. There are two types of views – function views and class views. They differ in whether they’re implemented as a function or as a class. We’re going to see examples of both later in the series.
Anyway, let’s have a closer look at the urlpatterns
list. It now contains one element, which is a path function. Here we have just one path function, but we’ll be adding more in the future.
Each path function takes two arguments. The first argument is a URL pattern. In this case it’s admin/
. The second argument may either of the following:
– a specific view, which is not the case here, or
– another list of URL patterns, which is the case here.
If the second argument is a specific view, this is the view that will be displayed in the browser when the URL is matched.
If the second argument is another list of URL patterns, it means there’s a list of URL patterns defined in another module. In this case, there’s a list of URL patterns in the admin.site.urls
module. Then the first argument becomes the base URL for the patterns defined in that module. And the Administration application has its own mapping system, which we will discuss later in the series.
As mentioned before, we could handle all URL mappings here, in the /portfolio/urls.py file or delegate some of them to the particular applications. We now have the catalog application that we just created, so let’s delegate the mapping to its own mapping system, which we are going to create. First, let’s import the include function from the django.urls
module and add a new path function to the urlpatterns
list. We usually do it using the +=
operator:
...
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += [
path('catalog/', include('catalog.urls')),
]
Now, when the catalog/
pattern is matched, the request will be forwarded to the catalog.urls
module.
Next, let’s take care of the root URL of our website. As we have just one application in our website, we want the root URL to redirect the request to that application. In other words, when we type in the address of the root in the browser:
127.0.0.1:8000 or localhost:8000
we want to be redirected to:
127.0.0.1:8000/catalog/ or localhost:8000/catalog/
To do that, we’ll need the RedirectView
class and its as_view
method:
...
urlpatterns += [
path('catalog/', include('catalog.urls')),
]
urlpatterns += [
path('', RedirectView.as_view(url='catalog/')),
]
The as_view
method takes the new relative URL as the first argument, which is the URL where we’ll be redirected when the pattern used as the first argument to the path function is matched, which is this case is an empty string, which corresponds to the root URL.
There’s one more thing we have to take care of. We want to make sure static files (CSS, JavaScript, images) are handled correctly. To ensure that, we have to add one more element to the urlpatterns
list, a static
function:
...
from django.views.generic import RedirectView
from django.conf import settings
from django.conf.urls.static import static
...
urlpatterns += [
path('', RedirectView.as_view(url='catalog/')),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Let’s simplify the code a little bit by putting all these elements in one list definition like so:
...
from django.contrib import admin
from django.urls import path, include
from django.views.generic import RedirectView
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('catalog/', include('catalog.urls')),
path('', RedirectView.as_view(url='catalog/')),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
But we still haven’t implemented the URL mapping system we’ll be delegated to. Let’s do it now. Inside the catalog folder create a urls.py file:
For now we’ll only create an empty urlpatterns
list here:
from django.urls import path
from . import views
urlpatterns = [
]
We’ll be adding all the patterns here as we proceed.
Database Migrations
Our website will present data, like the projects we want to show off, categories, links, images, text descriptions and so on. As mentioned before, this data will sit in a database. But we don’t have to worry about pushing it to the database and pulling it out of it. This is because there’s a tool that will take of that, the so-called Object-Relational-Mapper (or ORM for short). It maps models that we define in code to tables in the database. If we make any changes to the models, Django creates migration scripts that update the database.
We haven’t created any models yet, but some admin-related ones were created automatically when we created the website. In order to define tables in the underlying database for these models, we have to create and run migrations. Type the following commands in the terminal:
python manage.py makemigrations
python manage.py migrate
The first command creates the migrations. The second one applies them to the database.
We’ll be creating and applying migrations each time we modify our models.
Let’s test our website. Run the following command in the terminal to start the development web server:
python manage.py runserver
If you now open your browser and navigate to http://127.0.0.1:8000/
, you will be redirected to the catalog
page and you’ll see an error:
We see the error because we haven’t defined any urls in the catalog.urls
module. But at least we can see a detailed description of the error, which is only available in debug mode. We’ll add all the necessary urls later in the series. In the next part, we’ll create our models, though.