Launching Django applications
Tags: django, tutorial, python, productivity
Published: 2021-02-21 16:29:58

Deployment day, and it's Django. Where to start. We got a server we can SSH into, we got our code and endpoints - if any- ready to be consumed.

Right here, we are going to look into the best practices in terms of developing our backend applications, or SSRs(if we were using Django templating). Through the process, we elaborate on the whys of having a production-ready application as soon as we create our project as opposed to waiting on the completion of the whole development process.

#### Why now?

For one, a good product is never really finished. There is always that one UI change, that one bug or logic discrepancy, or better yet, user feedback prompting a feature addition - our application must be getting some traction. Look at this in this way, would we wait till we got all our user feedback before optimizing our application for production? What about how we serve our media files? Would we still be serving it directly all through? Would we have passwords to our databases hardcoded? What if I add a feature or adjust a UI on my local setup? Would it break the main production app right off the batt?

A pointer we are getting to is this: launch your application and make small incremental changes as you go. There is no 'Tadaaa! It works and will never break or be changed!' moment. If you were simply coding as though it was on your machine all through and waiting for the 'perfect' opportunity, you have to STOP. Stop it. Get your application and pipelines up as soon as you start the project. Be production-ready with 'hello world'.

Enough talk. Let us have something we can work with here. We create a virtual environment, install our dependencies, and have our boilerplate project.

```bash
GreenCodes ➤ mkdir launch-ready                                                                                                                                       
GreenCodes ➤ cd launch-ready                                                                                                                                          
launch-ready ➤ python -m venv .venv                                                                                                                                   
launch-ready ➤ source .venv/bin/activate                                                                                                                              
(.venv) launch-ready ➤
```

We've made the directory called `launch-ready`, our intended application name. We've also created and activated the virtual environment. Go ahead and install Django and create the project while at it.

```bash
pip install django
django-admin startproject LaunchReady .  
```
Our directory should now look like this:
```bash
(.venv) launch-ready ➤ ls                                                                                                                                             
LaunchReady  manage.py
(.venv) launch-ready ➤ ls LaunchReady                                                                                                                                 
asgi.py  __init__.py  settings.py  urls.py  wsgi.py
(.venv) launch-ready ➤
```

Running `python manage.py runserver` will give the default launch page for Django.

Our application is up and running. To prepare for production, however, we have a couple of things to configure.

### Create production and development settings.
The `LaunchReady` directory has one setting file. Within this directory, we create yet another directory named `settings` and move our settings file into it, renaming it in the process to `base.py`.

```bash
mkdir settings
mv -i settings.py settings/base.py
```
Create two additional files within the `settings` directory and name them `dev.py` - to contain development settings - and `prod.py` - to have production settings. Our directory ends up looking as below:

```bash
(.venv) settings ➤ ls                                                                                                                                                 
base.py  dev.py  prod.py
```

Locally, on our development machine, we will opt for our local installation of MySQL. Go ahead and create a database and user then head over back for the next step. 

As with best practice in terms of version control, **never store your passwords in code**. Opt at all times, for the environment variables. To manage this, we create a `.env` file that will never make it past our local machine. Just to be sure, we add it to the `.gitignore` file.

```bash
echo .env >> .gitignore
```

We then, go ahead and install  `python-dotenv` (to read the variables from the .evn file )and `mysqlclient` (a wrapper  to interact with MySQL from Django). 

```bash
pip install python-dotenv mysqlclient
```

Our development settings will look as below.


**dev.py**
```python
from dotenv import load_dotenv

from LaunchReady.settings.base import *

load_dotenv()

# since it's running on my machine, show me the errors
DEBUG = True

SECRET_KEY = os.getenv("SECRET_KEY")


DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": os.getenv("DATABASE_NAME"),
        "USER": os.getenv("DATABASE_USER"),
        "PASSWORD": os.getenv("DATABASE_PASSWORD"),
        "HOST": os.getenv("DATABASE_HOST")
    }
}

# show mail messages on the terminal
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

# run on every host.
ALLOWED_HOSTS = ["*"]


```

So we have our environment variables being read from within the development settings, but where do we set these items. Simple. We create a `.env` file next to our `dev` settings. 

```bash
(.venv) settings ➤ ls  -a                                                                                                                                               
base.py  dev.py  prod.py .env
```

The file content?

```txt
SECRET_KEY = 'django-generated-secret-key'
DATABASE_NAME=database_name
DATABASE_USER=database_user
DATABASE_PASSWORD=database_password
DATABASE_HOST=localhost
```

> Note: We can have multiple ways with the database and secret key configuration. It is not linear.


For our production application, we take the example off of Heroku. This works the same as with any other server we might get, with one or two tweaks.

We install `dj-database-url` - a python package that lets us read and perform actions on the database in the event it was on a separate platform or other from our main application.

```bash
pip install dj-database-url
```

This, we use in our production-ready settings as below:

**prod.py**

```python
import dj_database_url
from LaunchReady.settings.base import *


ADMINS = (("Developer name", "Developer email"),)

# always set this to false in production
DEBUG = False

SECRET_KEY = os.environ["SECRET_KEY"]

ALLOWED_HOSTS = ["launch-ready-domain.com", "server-ip-address"]

DATABASES = {}
DATABASES["default"] = dj_database_url.config(conn_max_age=600, ssl_require=True)


# ToDo: get an email host provider
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.gmail.com"
EMAIL_HOST_USER = os.environ["EMAIL_HOST_USER"]
EMAIL_HOST_PASSWORD = os.environ["EMAIL_PASSWORD"]

EMAIL_PORT = 587
EMAIL_USE_TLS = True

```

As our `DEBUG` is set to `False`, we, instead of getting the *not found* error, want to get a notification via mail. This is especially so when one of our views returns an exception. In this case, we set the developer(s) responsible.

```bash
ADMINS = (("Developer name", "Developer email"),)
```
As we have our Linux server up. We create persistent environment variables, this time, read from the bash configuration files, setting them in either, `/etc/environment` (for system-wide environment configuration), or in `~/.bashrc`- for per-user configuration in case we have multiple.

** /etc/environment**
```bash
SECRET_KEY=''
DATABASE_URL=''
# this is common with heroku and can be set via the console or dashboard interface
DJANGO_SETTINGS_MODULE=''
EMAIL_HOST_USER=''
EMAIL_PASSWORD=''
```

The only major difference from the previous development setting would be how we read our database configuration. 

```python
DATABASES = {}
DATABASES["default"] = dj_database_url.config(conn_max_age=600, ssl_require=True)
```
This would be how we configure our application to read from an external database from our application server location.

Let's try this run. Go to your shell and run your development server. Remember, we nested and split our settings. So to run anything while in development, we have to remind Django which settings we want to use.

```bash
python manage.py runserver --settings=LaunchReady.settings.dev
```


![Screenshot_20210221_170356.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1613916329225/RtQoundIA.png)


Our application default template is back up. 
For production, we would have it run as below.

```bash
python manage.py runserver 0.0.0.0:8000 --settings=LaunchReady.settings.prod

```

Launch your browser and head to `http://launch-ready-domain.com:8000`. You should get the very same page as with your development environment.


### What next?
Of course, there's more in terms of deployment server settings and configuration, especially in terms of serving static files. This, however, can be left for the next hour. We touch base with this rather lengthy piece as we fuel up and do this further in the next, and yes, the code can be accessed right off [TheGreenCodes](https://github.com/TheGreenCodes/LaunchReady) repository.

Till next time,

[TheGreenCodes](https://thegreencodes.com/)