Parts of this chapter is based on the django-marcadortutorial licensed under Creative CommonsAttribution-ShareAlike 4.0 International License. The django-marcador tutorialis copyrighted by Markus Zapke-Grndemann et al.
Choose the newest version available for your operating system. Download the installer, run it and then follow the instructions available here: -postgresql/. Take note of the installation directory as you will need it in the next step (typically, it's C:\Program Files\PostgreSQL\9.3).
Next up, we need to create our first database, and a user that can access that database. PostgreSQL lets you create as many databases and users as you like, so if you're running more than one site you should create a database for each one.
Replace name with your own name. You shouldn't use accented letters or whitespace (e.g. bożena maria is invalid - you need to convert it into bozena_maria). If it goes well, you should get CREATE ROLE response from the console.
Remember to replace name with the name you've chosen (e.g. bozena_maria). This creates an empty database that you can now use in your project. If it goes well, you should get CREATE DATABASE response from the console.
This tutorial will create a new Django project using Docker and PostgreSQL. Django ships with built-in SQLite support, but even for local development, you are better off using a "real" database like PostgreSQL that matches what is in production.
It's possible to run PostgreSQL locally using a tool like Postgres.app,; the preferred choice among many developers today is to use Docker, a tool for creating isolated operating systems. The easiest way to think of it is as a large virtual environment that contains everything needed for our Django project: dependencies, database, caching services, and any other tools required.
A big reason to use Docker is that it completely removes any issues around local development setup. Instead of worrying about which software packages are installed or running a local database alongside a project, you run a Docker image of the entire project. Best of all, this can be shared in groups, simplifying team development.
Docker Compose is an additional tool that is automatically included with Mac and Windows downloads of Docker. However, if you are on Linux, you must add it manually. You can do this by running the command sudo pip install docker-compose after completing your installation.
Hopefully, Docker will be installed by this point. To confirm the installation was successful, quit the local server with Control+c and then type docker run hello-world on the command line. You should see a response like this:
The code for this project can live anywhere on your computer, but the Desktop is an accessible location for teaching purposes. On the command line, navigate to the desktop and create a new directory called django-docker.
Next, we can create a new project called django_project, migrate our database to initialize it and use runserver to start the local server. I usually don't recommend running migrate on new projects until after a custom user model has been configured, but in this tutorial, we will ignore that advice.
How do we know the virtual environment is no longer active? There will no longer be parentheses around the directory name on the command line prompt. Any standard Django commands you try to run at this point will fail. For example, try python manage.py runserver to see what happens.
A Docker image is a read-only template that describes how to create a Docker container. The image is the instructions, while the container is the actual running instance of an image. To continue our apartment analogy from earlier in the chapter, an image is the blueprint or set of plans for building an apartment; the container is the actual, fully built building.
Images are often based on another image with some additional customization. For example, there is a long list of officially supported images for Python depending on the version and flavor of Python desired.
We need to create a custom image for our Django project that contains Python but also installs our code and has additional configuration details. To build our image, we make a file known as a Dockerfile that defines the steps to create and run the custom image.
Dockerfile's are read from top-to-bottom when an image is created. The first instruction is a FROM command that tells Docker what base image we want to use for our application. Docker images can be inherited from other images, so instead of creating our base image, we'll use the official Python image with all the tools and packages we need for our Django application. In this case, we're using Python 3.10.2 and the much smaller in size slim variant that does not contain the common packages contained in the default tag. The tag bullseye refers to the latest stable Debian release. Set this explicitly to minimize potential breakage when there are new releases of Debian.
The command WORKDIR sets a default working directory when running the rest of our commands. This working directory is used by Docker as the default location for all subsequent commands. As a result, we can use relative paths based on the working directory rather than typing out the full file path each time. In our case, the working directory is /code, but it can often be much longer and something like /app/src, /usr/src/app, or similar variations depending upon the specific needs of a project.
The next step is to install our dependencies with pip and the requirements.txt file we created. The COPY command takes two parameters: the first parameter tells Docker what file(s) to copy into the image, and the second parameter tells Docker where you want the file(s) to be copied to. In this case, we are copying the existing requirements.txt file from our local computer into the current working directory, which is represented by ..
Once the requirements.txt file is inside the image we can use our last command, RUN, to execute pip install. This command works exactly the same as if we were running pip install locally on our machine, but this time the modules are installed into the image. The -r flag tells pip to open a file--called requirements.txt here--and install its contents. If we did not include the -r flag pip would try and fail to install requirements.txt since it isn't itself an actual Python package.
At the moment, we have a new image based on the slim-bullseye variant of Python 3.11.5 and have installed our dependencies. The final step is to copy all the files in our current directory into the working directory on the image. We can do this by using the COPY command. Remember it takes two parameters so we'll copy the current directory on our local filesystem (.) into the working directory (.) of the image.
A .dockerignore file is a best practice way to specify certain files and directories that should not be included in a Docker image. This can help reduce overall image size and improve security by keeping things that are meant to be secret out of Docker.
We can safely ignore the local virtual environment (.venv), a future .git directory, and a future .gitignore file. In your text editor, create a new file called .dockerignore in the base directory next to the existing manage.py file.
We now have complete instructions for creating a custom image, but we haven't built it yet. The command to do this is docker build followed by the period, ., indicating the Dockerfile in the current directory. There will be a lot of output here. I've only included the first two lines and the last one.
Our fully built custom image is now available to run as a container. To run the container we need a list of instructions in a file called docker-compose.yml. Create a docker-compose.yml file with your text editor in the project-level directory next to the Dockerfile. It will contain the following code.
Docker recently switched away from Compose V1 to Compose V2. To use V2, we specify which services (or containers) we want running within our Docker host. It's possible to have multiple services running, but we have one for web for now.
Within web, we set build to look in the current directory for our Dockerfile. We'll use the Django default ports of 8000 and execute the command to run the local web server. Finally, the volumes mount automatically syncs the Docker filesystem with our local computer's filesystem. If we change the code within Docker, it will automatically be synced with the local filesystem.
Django is now running purely within a Docker container. We are not working within a virtual environment locally. We did not execute the runserver command. Our code now exists, and our Django server runs within a self-contained Docker container. Success!
Stop the currently running container with Control+c (press the "Control" and "c" button at the same time) and additionally type docker-compose down. Docker containers take up a lot of memory, so it's a good idea to stop them when you're done using them. Containers are meant to be stateless, so we use volumes to copy our code over locally, where it can be saved.
Whenever any new technology is introduced, there are potential security concerns. In Docker's case, one example is that it sets the default user to root. The root user (also known as the "superuser" or "admin") is a special user account used in Linux for system administration. It is the most privileged user on a Linux system and can access all commands and files. The Docker docs contain an extensive section on security and specfifically on rootless mode. We will not be covering it here since this is a book on Django, not Docker, but if your website stores sensitive information, review the entire security section closely before going live.
It's important to pause right now and think about what it means to install a package into Docker instead of a local virtual environment. In a traditional project, we'd run the command python -m pip install "psycopg[binary]" from the command line to install Pyscopg. But we're working with Docker now.
d3342ee215