REST API for candy shop deliveries (TASK).
- python3
- Django
- Django REST framework
- PostgreSQL
- Gunicorn
- nginx
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.
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 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
- 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 [email protected]: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
- 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
- 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
- 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
The app features several more useful endpoints aside from the task, e.g.
GET /courier-types
– list of all courier typesPOST /courier-types
– add a new courier type, e.g.
{
"courier_type": "scooter",
"capacity": 10,
"earnings_coef": 7
}
GET /couriers
– list of all couriersGET /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 :(