Upgrading GeoSquare to latest Django
When trying to play with the latest fasthtml framework launched recently, I did not want to use a service provider like vercel or railway and wanted to deploy on my ec2 instance at Amazon. The problem was my current django apps that host geosq.com were running very old versions of python(I know, I know)
Previous setup
- Python 2.7
- Django
- uWSGI
uWSGI was running as emperor and had multiple Django apps that were all Python 2.7
Current Setup
The main goal for this go-around is to isolate each app under its own environment. Each of the sites will have its own service so they can be started and shutdown individually
- Miniconda
- Python 3.11
- Django
- Gunicorn
The file to host the individual django app will be
First create a conda environment
conda create -n geosq python=3.11
Install the required packages
conda activate geosq
pip install django
pip install mysqlclient
Then create a service
sudo vi /etc/systemd/system/gunicorn_geosq.service
[Unit]
Description=Gunicorn daemon for geosq Django application
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/geosq
ExecStart=/home/ubuntu/miniconda3/envs/geosq/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/ubuntu/geosq/geosq.sock geosq.wsgi:application
[Install]
WantedBy=multi-user.target
Reload the daemon
sudo sudo systemctl daemon-reload
Bring up the service
sudo systemctl start gunicorn_geosq
Check status
sudo systemctl status gunicorn_geosq
If everything is good, enable so it starts at boot time
sudo systemctl enable gunicorn_geosq
Django code Upgrade
I used mostly Claude 3.5 Sonnet to quickly port from the old Django to the lastest version. The only tricky part was to create a local test database when upgrading locally on my Mac. For this I used sqlite in dev and using an environment variable to connect to MySql in prod.
The database configuration would be driven by DEBUG
variable
DEBUG = os.environ.get('DJANGO_DEBUG', 'True') == 'True'
# Database configuration
if DEBUG:
# Development database (SQLite)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'geosq.sqlite3',
}
}
else:
# Production database (MySQL)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('DB_NAME', 'yourdatabasename'),
'USER': os.environ.get('DB_USERNAME', 'yourusername'),
'PASSWORD': os.environ.get('DB_PASSWORD', 'yourpassword'),
'HOST': os.environ.get('DB_HOST', ''),
'PORT': os.environ.get('DB_PORT', ''),
}
}
This way I could test the functionality before migrating to production.
nginx and Gunicorn
When you start Gunicorn service for GeoSquare, It will create a sock file which needs to be presented to nginx so it can route the traffic. Here is how I did it. In addition you can serve static files using nginx which is much more efficient than python, so you would intercept any /static
GET calls and serve them directly.
server {
server_name www.geosq.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/ubuntu/geosq;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/home/ubuntu/geosq/geosq.sock;
}
listen 80;
}