Skip to content

KonstantAnxiety/CandyDeliveryApp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 

Repository files navigation

Candy Delivery REST API

REST API for candy shop deliveries (TASK).

Technologies used

  • python3
  • Django
  • Django REST framework
  • PostgreSQL
  • Gunicorn
  • nginx

Database diagram

The database enables more flexibility than it is needed for the task, e.g. new courier types can be added to the reference table CourierType.

DB diagram

Deploy

This instruction describes how to install the app with a new virtual environment, configure PostgreSQL database, Gunicorn as a WSGI HTTP server to listen to requests on bootup and nginx as a proxy server on Ubuntu 20.04.2, assumes that the server has a user with sudo privileges named user and features nano as a text editor.

Install packages

  • Install python3
  • Install needed and useful packages
sudo apt install git htop python3-pip python3-dev python3-virtualenv postgresql postgresql-contrib libpq-dev nginx curl
  • You may want to change the password for the default postgres user
sudo passwd postgres
  • Upgrade pip
python3 -m pip install --upgrade pip

Configure Django app and PostgreSQL

  • Change to home directory
cd
  • Let's assume we want to store apps in a distinct directory
mkdir webapp
cd webapp/
  • Generate a new rsa key pair and add the public key to GitHub
ssh-keygen
cat ~/.ssh/id_rsa.pub
  • Get the repository
git clone git@github.com:KonstantAnxiety/CandyDeliveryApp.git candydelivery
cd candydelivery/
git status
  • Create a virtual environment, activate it and install required packages
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
  • Log into Postgres session, create the database and a new user for the django app
sudo -u postgres psql
postgres=# CREATE DATABASE cda_db;
postgres=# CREATE USER sampledbuser WITH PASSWORD 'SampleDBUserPass';
postgres=# ALTER ROLE sampledbuser SET client_encoding TO 'utf8';
postgres=# ALTER ROLE sampledbuser SET default_transaction_isolation to 'read committed';
postgres=# ALTER ROLE sampledbuser SET timezone TO 'UTC';
postgres=# GRANT ALL PRIVILEGES ON DATABASE cda_db to sampledbuser;
postgres=# \q
  • To let the app interact with the database make the following changes to ~/webapp/candydelivery/cda/cda/settings.py (highlighted lines imply individual information)
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
!        'NAME': 'cda_db',
!        'USER': 'sampledbuser',
!        'PASSWORD': 'SampleDBUserPass',
        'HOST': 'localhost',
        'PORT': '',
    }

}
...
  • Do not use the secret key from the repo, instead generate a new one for the app with
python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'

And change the SECRET_KEY in settings.py, make sure to keep it secret.

  • Migrate the database and load initial data
cd cda/
python manage.py makemigrations
python manage.py migrate
python manage.py loaddata initial_data
  • You may want to create a django superuser with
python manage.py createsuperuser
  • If you want the app to feature static files, you need to collect them with
python manage.py collectstatic
  • The virtual environment may now be deactivated
deactivate

Configure Gunicorn

  • Create a socket file for gunicorn
sudo nano /etc/systemd/system/gunicorn.socket

Add the following to the file

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target
  • Create a service file for Gunicorn
sudo nano /etc/systemd/system/gunicorn.service

Add the following to the file (highlighted lines imply individual information)

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
!User=user
Group=www-data
!WorkingDirectory=/home/user/webapp/candydelivery/cda
!ExecStart=/home/user/webapp/candydelivery/venv/bin/gunicorn \
          --access-logfile - \
!          --workers 9 \
          --bind unix:/run/gunicorn.sock \
          cda.wsgi:application

[Install]
WantedBy=multi-user.target
  • Start the socket with
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

Troubleshoot using

sudo systemctl status gunicorn.socket

At first the socket may be dead. You can wake it up with

curl --unix-socket /run/gunicorn.sock localhost

You should see some html (Not found, because the root page is not implemented in the app)

Now the socket should be active when you execute

sudo systemctl status gunicorn.socket
  • Troubleshoot with
sudo journalctl -u gunicorn.socket
sudo journalctl -u gunicorn

Configure nginx

  • Assuming you want to modify the default server block
sudo nano /etc/nginx/sites-available/default

Add the following to the file (highlighted lines imply individual information)

server {
        listen 8080 default_server;
        listen [::]:8080 default_server;

        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location /static/ {
!                root /home/user/webapp/candydelivery/cda;
        }

        location / {
                include proxy_params;
                proxy_pass http://unix:/run/gunicorn.sock;
        }
}
  • Restart nginx with
sudo service nginx restart
  • Troubleshoot with
sudo systemctl status nginx
sudo journalctl -u nginx

Tests

  • Change to the repo directory
cd ~/webapp/candydelivery
  • Activate the virtual environment
source venv/bin/activate
  • Change to the app directory
cd cda/

This app features Django's unit tests.

To run the tests use the following command while in the same directory as manage.py

python3 manage.py test
  • To see a more verbose report use
coverage run manage.py test
coverage report

Comments for backend-school

The app features several more useful endpoints aside from the task, e.g.

  • GET /courier-types – list of all courier types
  • POST /courier-types – add a new courier type, e.g.
{
   "courier_type": "scooter",
   "capacity": 10,
   "earnings_coef": 7
}
  • GET /couriers – list of all couriers
  • GET /orders – list of all orders

Wow I wanted to take a look at my code for the last time and I've just noticed that I forgot to validate that courier_id is positive. I do not want to leave it like that so I hope that you can find it in your heart to forgive me and let me fix this at least in the repo, besides it is just a line :(