We create a blog on Django with polls and tests. Brief instruction. Part 2

In the previous part, we partially parsed the template for our blog, selected a virtual machine and launched a native Django web server on it. However, it is only intended for testing and running applications during development. To process requests in production, you need to configure Nginx and WSGI Gunicorn. This article shows how to do it.

Adding new features

Before launching a blog in production, it is better to make sure that the necessary functionality is implemented. In the first part, we collected only basic Django applications – users And blog. But we have not added the rest of the features yet – it’s time to fix it.

Below we analyze templates for creating polls and tests in a Django blog. Do fork of source codemodify and offer your improvements!

poll application

In the application polls all the functionality related to conducting surveys is concentrated. To create it, you need to run the manage.py control script and register a new application.

manage.py startapp polls

It is also important to register the new application in the project’s root directory, in the settings.py configuration file.

In order for the user to connect to the application, you need to configure routing – go to the root directory of the project and register the paths in the urls.py configuration file.

path('', include('polls.urls')) 

The polls application’s own routes will be stored in the users/urls.py file – they can be found and modified by link.

The polls application visualizes poll results using the Chart.j library: the charting logic is described in the function results.

def results(request, question_id):
    profile = request.user.profile
    question = get_object_or_404(Question, pk=question_id)
    labels = []
    data = []
    votes = question.choice_set.select_related('question').all() 

    for item in votes:
        labels.append(item.name)
        data.append(item.votes)

    context = {	'question': question, 
    			'profile': profile, 
    			'labels': labels, 
    			'data': data}    
    return render(request, 'polls/results.html', context)

If possible, move the logic of validators from views into separate modules.

All the necessary templates are in the directory polls/emplates/polls:

user_voted method class Question checks if the user has taken part in the poll (you can only vote once). The polling logic is described in polls/views.py. The vote function provides for cases where the user did not select any answer option, and when he has already taken part in the poll.

app quizzes


Application quizzes provides the creation and processing of two types of surveys – with one and with several correct answers. Similarly, we create it, register it in settings.py and initialize it in urls.py of the root directory. The routes of the blog itself are in quizzes/urls.pyand the templates are in quizzes/templates/quizzes.

The poll module uses four templates:

  • display.html – shows the question and answer options,
  • quizzes.html – is responsible for the pagination of the created tests,
  • results.html – Displays test results.
  • partial.html – provides – together with the JS script in the display.html file – the output of feedback without reloading the page,

The Question class in quizzes/models.py contains two helper methods:

  • get_answers – returns the correct answers, depending on the type of question,
  • user_can_answer Determines if the user has answered this question before.

Presentation functions in quizzes/views.py are responsible for the sequential output of test questions and the evaluation of user responses. Saving statistics on correct and incorrect answers is implemented using the class Result. In this case, updating the values ​​in the database is carried out using the object F.

result, created = Result.objects.get_or_create(user=request.user, 
    quiz=quiz)
if is_correct is True:
    result.correct = F('correct') + 1
else:
    result.wrong = F('wrong') + 1

Done – now you can send the blog to production!

Updating files on the server

In the previous part, we have already rented a virtual server with flexible core performance and uploaded the project code. Now it needs to be updated to deploy the new polls and quizzes applications. To do this, you need to push them to the repository on GitHub, and then do git pull on the server side.

Configuring Nginx and Gunicorn


When serving a Django application, Nginx acts as a reverse proxy server: it is responsible for processing incoming requests on port 80 and forwarding them to Gunicorn.

Gunicorn, on the other hand, plays the role of an application server: it is responsible for starting Django, processing requests and generating responses. As a result, the system works like this:

  1. The client sends a request to the Nginx server,
  2. Does Nginx forward (proxy) the request to Gunicorn using the proxy pass directive?
  3. Gunicorn passes user request to Django,
  4. Django processes the request, generates a response, and sends it to Gunicorn,
  5. Gunicorn sends the result to Nginx, which forwards it to the client.

With this configuration, Nginx provides fast and efficient request processing, and Gunicorn allows you to run multiple processes to process requests in parallel and is responsible for automatic restart and smooth shutdown of worker processes.

Installing and testing Gunicorn

There is nothing complicated in the installation: just activate the virtual environment and install the necessary dependencies.

source blogitenv/bin/activate
pip install gunicorn

Check and change settings if necessary config/settings.py, as shown in the previous section. And then create a superuser and a database and collect the static files.

python3 manage.py migrate
python3 manage.py createsuperuser
python3 manage.py collectstatic

Now you can test how Gunicorn works by running it in conjunction with a Django application.

gunicorn --bind 0.0.0.0:8000 config.wsgi

Everything works: you can connect to the blog by IP address through port 8000. But now the web server is started manually. In production, this should happen automatically – you need to configure Gunicorn and Nginx to serve the Django application together.

You may also be interested in these texts:

→ Simple procedural world generation, or Perlin Noises in Python
→ “Pocket weather forecaster for an hour.” We write a Telegram bot for weather monitoring in Python
→ We check the Old Testament history of the origin of mankind from Adam and Eve using a population model

Gunicorn setup

To automate the launch of a Django application, we first need to create the gunicorn.socket and gunicorn.service configuration files. Let’s start with the first one:

sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=Gunicorn socket
 
[Socket]
ListenStream=/run/gunicorn.sock
 
[Install]
WantedBy=sockets.target

Contents of the gunicorn.socket file

Now let’s create gunicorn.service and point to the virtual environment that the web server should run.

sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn daemon
Requires=gunicorn.socket
After=network.target
 
[Service]
User=root
Group=www-data
WorkingDirectory=/root/blogit
ExecStart=/root/blogitenv/bin/gunicorn \
      	--access-log file - \
      	--workers 5 \
      	--bind unix:/run/gunicorn.sock \
      	config.wsgi:application
 
[Install]
WantedBy=multi-user.target

Contents of the gunicorn.service file

Now you can start Gunicorn and check its status.

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
sudo systemctl start gunicorn.service
sudo systemctl enable gunicorn.service

sudo systemctl status gunicorn

A properly configured server will return a status of running and display information about workers – instances of a Django application running in parallel. In the gunicorn.service configuration file, we have registered five “workers” – Gunicorn should start them.

Request proxying

The last step is to go to the Nginx server settings and configure request proxying from port 80 to Gunicorn.

sudo nano /etc/nginx/sites-available/blogit
server {
	listen 80;
	server_name 94.26.224.162;
	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;
 
	location = /favicon.ico {
    	alias /root/blogit/static/favicon.ico;
	} 
 
	location /static/ {
    	alias /root/blogit/static/;
	}
 
	location /media/ {
    	alias /root/blogit/media/;
	}
 
	location / {
    	include proxy_params;
    	proxy_pass http://unix:/run/gunicorn.sock;
	}
}

The contents of the /etc/nginx/sites-available/blogit. For server_name, enter the public IP address of your virtual machine.

To apply the configurations, link the Nginx directives – sites-available and sites-enabled – using a symbolic link:

sudo ln -s /etc/nginx/sites-available/blogit /etc/nginx/sites-enabled/

Note: if an error is found in the configuration files later, the link must be recreated, but with the -sf flag.

sudo ln -sf /etc/nginx/sites-available/blogit /etc/nginx/sites-enabled/
sudo systemctl reload nginx

Also, if you are deploying a project as non-root, you need to assign permissions to the static and media directories.

chown -R django_user:www-data /var/www/blogit/static/
chown -R django_user:www-data /var/www/blogit/media

This completes the setup of Nginx and Gunicorn. Now you can open the site through a browser. If you wish, you can rent a domain name and generate an SSL certificate.

We still have developed only a small part of the blog functionality. For example, you can set up password recovery using email messages using the Selectel mail service (free during beta).

What features would you add? Share your ideas in the comments!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *