
Deploy python django app on render platform at free of cost.
The prerequisites:
- A python django app
- Python3
- Django
- Account in Render platform
- Package to be installed: gunicorn, whitenoise, dj-database-url, psycopg2-binary
- Dbeaver, a desktop app to conect different type of databses
The Django App Structure:
For example we have a django app having the below files and directories structure:
MYAPP/
│
├── app/
│ ├── migrations/
│ │ └── __init__.py
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── test.py
│ └── views.py
│
├── myapp/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│
├── manage.py
│
├── Pipfile
└── Pipfile.lock
Steps to get ready to deploy your app:
-
Sign up to render.com
-
Create a Requirements File: We can generate a requirements.txt file in the root directory which listing the project dependencies by using the commandline:
# pip freeze > requirements.txt
-
Configure Django for Production: Update your settings.py file for production. Make sure to set DEBUG to False
-
Configure static file:
- Go to myapp
- Open settings.py module
- At the bottom, there is a variable called STATIC_URL. Add a new variable STATIC_ROOT and set this to the path of the folder where it contains our static file. Currently we do not have that so,
- Add a folder named “static” at the root of the project
- Now put the static folder path in STATIC_ROOT variable, but not hardcoded. Rather use the join() method: os.path.join(BASE_DIR, “static”).
- Save the changes.
- Now open the terminal window and run:
python3 manage.py collectstatic
This command will look at all the installed apps it will get all their static files and copy to the static folder.
We will see a message like this in output: 125 static files copied to '/Users/user/myapp/static'.
5. Install gunicorn: The Gunicorn “Green Unicorn” is a Python Web Server Gateway Interface HTTP server. Run:
penv install gunicorn
- Create Procfile: Add a new file called “Procfile” at root directory. Note: spell this exactly the same and this is the file render looks at to start the application.
- In this file, write the below code:
web: gunicorn vidjan.wsgi
- Save the changes
- Database setup in setting.py module: Make sure the database list seems like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
- Create a New Web Service on Render:
- Log in to your Render account.
- Click on the “New” button and select “Web Service.”
- Connect your Git repository.
- Configure the following settings:
- Environment: Python
- Build Command: pip install -r requirements.txt
- Start Command: gunicorn yourprojectname.wsgi
- Click “Save” and then “Deploy.”
- Update Host in settings.py: In Django, the ALLOWED_HOSTS setting is a security measure to prevent HTTP Host header attacks. It specifies a list of valid host/domain names for your Django application. When Django receives a request, it checks the Host header against the values in ALLOWED_HOSTS. If the Host header does not match any of the allowed values, Django raises a SuspiciousOperation (400 Bad Request) exception.
So we need to copy the url from the render platform and update the allowed host in settings.py file. The code seems like:
ALLOWED_HOSTS = [myapp.onrender.com]
- Then git commit and push. Then we can see the website properbly.
Deploying the django app
Welcome to the documentation guide for deploying a Django application with a PostgreSQL database on the Render platform. This guide will walk you through the process of connecting Render’s PostgreSQL database to your Django app, ensuring a seamless deployment experience.
In this tutorial, you will learn the step-by-step instructions for deploying both the Django application and its associated database on the Render platform. By the end of this guide, you’ll have a fully operational Django app running on Render, with a connected PostgreSQL database.
Please follow the detailed instructions provided in each section to successfully set up and deploy your Django application on Render, utilizing the PostgreSQL database for data storage.
Let’s get started on this journey of deploying and hosting your Django app with Render!
Steps to follow:
- Login to render platform.
- Connect your github account to the render, this will allow deploy the app easily.
- Create a postgresql database on render:
- Go to render dashboard
- Navigate to new postgresql and it takes you inner page to create database
- Input the name for the database it is arbitrary
- Keep the other option as it is and choose the free plan
- And click on the creat database button
- It will take you to the confirmation page where you can see the status
- The postgresql databse has a option to connect let’s see what options are there in this page:
- Click on the Connect button on the write top corner
- It opens a try with 2 options:
- Internal connections: If my app is hosted on render as a web service then I need to use the internal connection URL.
- External connections: If I am connecting to the database from outside of render then need to use the external connection URL.
- Go back to the vscode and open settings.py module and we need connect the postgresql database to the django app by overwriting the DATABASE setting on settings.py module instead using the sqlite3. To do so we need to do a couple of things.
- The current database setup is like this below:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
* Install the dj-database-url class and run the command:
pipenv install dj-database-url
- After install done let’s import that on the top like:
import dj_database_url
- Then write database settings, we create a class instance for dj_database_url and use parse method that takes the external url for the render’s postgresql as parameter and it returns DATABASES[‘default’] value as below:
DATABASES['default'] = dj_database_url.parse('external_url_to_connect_postgresql_that_copied_from_render')
- Save the changes, and now we have a separate database and we need to migrate because we need to create tables to the new database. So run the command for the migration:
python3 manage.py migrate
- If it raises error like: error loading psycopg2 or psycopg module that means I do not have postgresql driver installed. So to install run the command:
pipenv install psycopg2-binary
or it could be: psycopg2 without the binary basde on the local machine.
- Then run the migrate again. This time migration is done.
- To connect to my database directly with a tool called Dbeaver, open the dbeaver desktop app and to connect, choose the postgresql in the dbeaver’s desktop app and I will see the database as postgresql after the migration.
- To get connect the database, we need a couple setup here as below:
- I copy paste the external url: postgres://vidjan_postgresql_user:gWB1CyEwJDVirOfS9YI1s9sUYxswXxMd@dpg-cmp46dicn0vc73cl2dog-a.singapore-postgres.render.com/vidjan_postgresql somewhere and then take out the things that should be the part of the aprppiate fileds like, Database name and so on.
- Databse name is: vidjan_postgresql
- The host should be everything after the @ sign like: dpg-cmp46dicn0vc73cl2dog-a.singapore-postgres.render.com
- Then take out the user name: vidjan_postgresql_user
- then after the colon sign to till @ sign should be the password: gWB1CyEwJDVirOfS9YI1s9sUYxswXxMd
- Then click on the test connection button and check it works and I see cconnected and click on the ok button.
- Then click on the finish button and it should be connected. And I should see the database.
- Let’s test the databse works or not. Open the server from the terminal:
python3 manage.py runserver
then add data then again open the Dbeaver and see the data is up here or not.
-
Now time to deply the django app:
-
Open the render dashboard.
-
Go for creating a new web service.
-
Here we need to connect github repo so we need to push all the changes to the github.
-
But before pushing in the settings.py module, we need to set certain things that should come from environment variables because we do not want that should go to the repo. The environment variables can be setup on render dashboard and we grab this in our codes.
-
First Import os, so that I can grab environment variables.
-
One thing that need to be in environment variable is DEBUG. Right now it is TRUE. But to run the app on production we need this to be set as False so we want this to be the value come from the environment veriable. so I can do like: call the get() method from os.environ module and that takse a key named “DEBUG” and it going to return ‘False’ by default so pass this as 2nd argument. So it looks like in the codes:
DEBUG = os.environ.get("DEBUG", "False")Now I want to check if this equal to ture as well so finally it comes like:DEBUG = os.environ.get("DEBUG", "False") == "True"So debug takes in a boolean so we want to set if the debug value itself is string that's true it will going to equal true. And actually we can set this in more better way, using lower method so that the thing will still work if the varibales is lower as well.DEBUG = os.environ.get("DEBUG", "False").lower() == "true" -
Second thing is ALLOWED_HOSTS, we can slso create environment variable. I want to call get() and set the key “ALLOWED_HOSTS”, and for example we want to use local host as well. So what I am going to do is, spliting the value with an space using .split(” ”), the I idea is when we have multiple hosts separated with a space and returns a list. So it will be like:
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "localhost 127.0.0.1").split(" ") -
Third things is SECRET_KEY, I can do something similar call the get() method on os.environ module and pass key as “SECRET_KEY” like:
SECRET_KEY = os.environ.get("SECRET_KEY").__str__instead passing the hardcoded actual key. Aslo We are using .str magic method referrence here, to return the value as string.
-
The last thing would be the DATABASE, we can set a vriable called databse_url and set this to environemnt variable by calling the get() method and pass the key “DATABASE_URL” then replace the string inside the parse method argument with the variable, database_url. The current codes:
DATABASES['default'] = dj_database_url.parse('external_url_to_connect_postgresql_that_copied_from_render')Replace codes with:
database_url = os.environ.get("DATABASE_URL") DATABASES['default'] = dj_database_url.parse(database_url) -
To use both databses, sqlite3 for local host and postgresql for render host we can use the if condition. This will check if the databse_url is ture for the environment variable settings on render end then database default value from the postgresql. else default is sqlite3 for local host. So the full codes look like:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } database_url = os.environ.get("DATABASE_URL") if database_url: DATABASES['default'] = dj_database_url.parse(database_url) -
Now we need requirements.txt file that contains list of all the dependencies because when render platform deploys this the platfrom needs to install all this dependencies. to create the txt file we need to run the command:
pip freeze > requirements.txt
This will create the file automatically in the root directory.
-
Now go ahead and push the changes in the github.
-
Now actually we can able to connect the github to render dashboard.
-
It takes you to the setup page to set the webservice name and others fields;
- Input the name in the name field for example: myapp
- Set the region
- Branch like master/main based on your github settings
- Root directory I keep empty for the time being
- Runtime to Python3 since the language of the app on python
- Buid command to pip install -r requirements.txt
- Start command actually going to run the app so we need gunicorn to be installed and it can run the wsgi. So we set this to: gunicorn then name_of_my_app then .wsgi like: gunicorn myapp.wsgi
- Then select the payment option set as free.
- Then click on the create button and it start try to deploy inside the inner page.
-
If I see any error we need to fix those in accordance with in the codes and push to github then come back to the render websevice page and manually deploy with latest commit.
-
There are few common issues at this point because we did not specify those in the render webservice environment variable option. So go to the Environment option in the dashboard and set as below;
- First key is the SECRET_KEY and copy the SECRET_KEY from the settings.py module and paste it in value.
- Then add another key which is ALLOWED_HOSTS and set the value as the render app web link and if you need multiple host like localhost specify with a space.
- And we need another one which is DEBUG and value should be set to False.
- And another one is DATABASE_URL and set the value as the internal database url copying from the postgresql page.
- And save the changes and it will automatically start deploying.
-
After deployment is done you head over to the url to open the app. But you might see a common error, 400 a bad request error. So we come to know somthing is not working properly but we are not sure what is the issue.
-
So for the time being we change the DEBUG value False to True on the emvironment setting to figure out what is going on. Now deployment automatically starts. And this time we can see detail error message and we can see for example, the host start with https we remove it and save the changes and it deploys automatically again.
-
Now the web app is working as expected and it is using the postgresql database as before we already have data in it.
-
And we can add data here and can see it has saved to the postgresql database.
Steps to add new data fields in local then migrate to production:
Up to this point, your app is live on render hosting. Now you change or add new data field in your app localy that could updates the local database, qslite3. Then test in local machine if things are okay then push and you need migrate local changes to the production. And the steps of full process as below:
-
To change the data fileds in your app:
-
Modify the models.py module by adding model class attribute
-
Update the Admin.py module in according to the changes on model class
-
Then update the html templates based on the data
-
Save the changes in local and run the local server and check every things work expectedly.
-
If things are okay then update the local database, sqlite3 by running the below command:
python3 manage.py makemigrations
Note: After the new data filed is added it might not allow you to make migrations because the data filed for privious data become none so you need to provide default data for the empty fields for previous data. When you run the make migrations command it prompts 2 options to provide the default data like below:
Please select a fix:
1. Provide a one-off default now (will be set on all existing rows with a null value for this column)
2. Quit and manually define a default value in models.py.
Then input the default value for the empty fileds in the terminal as instruction then complete the make migrations.
Then to migrate run:
python3 manage.py migrate
- Populate new data on the localhost app and check things are working and data is saving in the sqlite3 database.
- Now commit and push main to github this will automatically deploy on render end.
- Then you need to update the databse on the render end which is postgresql. To do so run the command:
export DATABASE_URL=external_database_url
You can copy the external_database_url from the postgresql database page on render postgresql pages.
- Then re-run migration:
python3 manage.py migrate
- Prouction databse is updated now with new data. And you can use the production now.