PostgreSQL Schemas / Namespaces with Django

Django and PostgreSQL


What about if you could organize your database objects (e.g. tables and views) in namespaces according with its roles in the system?
In this article we will see the right way to handle with PostgreSQL schemas in Django and some little tips about Django models and Python.


Also known as a namespace, schema is a type of database object whose purpose is to be a hierarchical organization layer that is just below a database.
On PostgreSQL, “public” is the default schema, but you can create your own namespaces to organize others kind objects such as tables, views, functions and etc.

Database Objects Hierarchy

- Server
   |- PostgreSQL Instance (Port 5432 by default)
       |- Role (Users and Groups)
       |- Tablespace
       |- Database
           |- Trigger
           |- Extension
           |- Language
           |- Schema      
               |- Table
               |- View
               |- Materialized View
               |- Sequence
               |- Function
               |- Procedure


About our Lab

That’s a simple lab with Django in a virtual environment (with virtualenv) and PostgreSQL installed in localhost.

  • Python 3.8
  • Django 3.0
  • PostgreSQL 12

Should work with many older versions 🙂


  • > SQL (psql);
  • $ shell (Linux, FreeBSD, Unix*);
  • >>> Python shell.



  • PostgreSQL

The database struture is the first thing that we will do.

  • Creation of database user for the application;
  • Database creation;
  • Schema creation;
  • Table creation


Let’s create our own example in psql build-in command line tool:

$ psql


Creation of user application:


The database role was created with an encrypted password and login attribute (user).


Creation of database for tests:

> CREATE DATABASE db_test OWNER user_test;

The database is owned by “user_test”.


Connect to it as user “user_test”:

> \c db_test user_test

Inside the psql shell \c database username.


Creation of a schema:


The namespace for our example is ready!


Show all schemas that aren’t catalogs:

nspname AS namespace
FROM pg_catalog.pg_namespace
WHERE nspname !~ '(^pg_|information_schema)';



Notice that appears the default namespace (public) and the ns_hr, created for our lab.


Creation of a table in ns_hr schema:

> CREATE TABLE ns_hr.tb_person(
    id_ serial primary key,
    name text not null,
    surname text not null

A simple table…

Hit <Ctrl> + D to exit.


  • Django

It’s time to code in Python! 😀

  • Virtual environment;
  • Python modules installation;
  • Django project creation and configuration;
  • Django app creation;
  • Django model creation;
  • Migrations;
  • Tests in shell;


Virtual environment creation:

$ virtualenv -p `which python3.8` django

The absolute path of binary of Python 3.8 was indicated as Python interpreter of this environment.


Access directory of environment and activate it:

$ cd django && source bin/activate

Your prompt changed, started at “(django)” indicating that your virtual environment was activated.


Install need modules for our tests:

$ pip install django psycopg2-binary configobj ipython

Respectivaly: Django web framework, PostgreSQL driver, configuration file reader and improved interactive shell.


New Django project creation:

$ django-admin startproject my_project


Rename the directory of project to src:

$ mv my_project src

This is for ease the directory hierarchy and will not affect the results. It’s because that has a same name directory that can cause some confusion…


Database configuration file creation:

$ cat << EOF > src/my_project/db.conf
DB_HOST = 'localhost'
DB_NAME = 'db_test'
DB_USER = 'user_test'
DB_PORT = 5432

Here we created a separated configuration file for the database connection.


Edit the main configuration file of the project:

$ vim src/my_project/


import os

from configobj import ConfigObj

Below the imports add a line that brings ConfigObj class.


# Database

# Database configuration file location
DB_CONF_FILE = f'{BASE_DIR}/my_project/db.conf'

# Read the configurations from file

# Database connection parameters


             'default': {
                         'ENGINE': 'django.db.backends.postgresql',
                         'NAME': DB_NAME,
                         'USER': DB_USER,
                         'PASSWORD': DB_PASSWORD,
                         'HOST': DB_HOST,
                         'PORT': DB_PORT,

Modify the database “session” as above.


Symbolic link creation for

$ ln -s `pwd`/src/ `pwd`/bin/

To ease our work we created a symbolic link to in bin directory that is in our $PATH.


Run virtual web server:

$ runserver

Test in your browser: http://localhost:8000 and then <Ctrl> + C to abort.


Access project directory:

$ cd src


Let’s check the files inside the current directory:

$ tree .


└── my_project
    ├── db.conf
    ├── __pycache__
    │   ├── __init__.cpython-38.pyc
    │   ├── settings.cpython-38.pyc
    │   ├── urls.cpython-38.pyc
    │   └── wsgi.cpython-38.pyc

List contents of current directory in a tree-like format.
Here we see all files inside the project.


First migration for Django metadata:

$ migrate


Creation of super user of Django:

$ createsuperuser


Create an app:

$ startapp human_resource


Edit to add new app:

$ vim my_project/
# Application definition


    # Custom Apps

A cool Django trick: you can use a directory models instead of a file
But you must create a dunder init file ( inside the models directory.
Let’s go!


Creation of models directory inside the the app directory:

$ mkdir human_resource/models


Remove the file:

$ rm -f human_resource/


Model creation:

$ vim human_resource/models/
from django.db.models import AutoField
from django.db.models import Model
from django.db.models import TextField

class Person(Model):
    Person Model

    Namespace: ns_hr
    Table: tb_person

    id_ = AutoField(db_column='id_', name='id', primary_key=True,)
    name = TextField(db_column='name', name='name',)
    surname = TextField(db_column='surname', name='surname',)

    def __str__(self):
        return f'{} {self.surname}'

    class Meta:
        db_table = 'ns_hr"."tb_person'  # 'schema"."object'
        verbose_name_plural = 'Person'

To enjoy the benefits of PostgreSQL schemas, inside your model, in Meta inner class, to the value of “db_table” attribute you must put a dot that separate namespace and object between quotation marks.


The object could be a table or a view, for example…


Dunder init inside the models directory fot the migrations take effect:

vim human_resource/models/
from import Person

This is necessary for the models directory works as file.


(No) Migrations: My Database, My Rules!

We create the structure of our database and no ORM should do it for us!
We have the power!
We have the might!
We are in command!

Our database, our rules! 😉

Just model your database by your own hands and make a fake Django migration.
Because only we know how the database objects must be created 😉


Make migrations for human_resource app:

$ makemigrations human_resource


Fake migration:

$ migrate --fake


Let’s check the directory hierarchy of the app:

$ tree human_resource/
├── migrations
│   ├──
│   ├──
│   └── __pycache__
│       ├── 0001_initial.cpython-38.pyc
│       └── __init__.cpython-38.pyc
├── models
│   ├──
│   ├──
│   └── __pycache__
│       ├── hr.cpython-38.pyc
│       └── __init__.cpython-38.pyc
├── __pycache__
│   ├── admin.cpython-38.pyc
│   └── __init__.cpython-38.pyc


Django Shell (Ipython):

$ shell
>>> from import Person

>>> p = Person(name='Ludwig', surname='van Beethoven')                                                                         

>>> print(p)


Ludwig van Beethoven
>>>   # Persist in database

Hit <Ctrl> + D to exit!


Database shell (psql):

$ dbshell

A query to check if the data was inserted by Django:

>  SELECT id_, name, surname FROM ns_hr.tb_person;


 id |  name  |    surname    
  1 | Ludwig | van Beethoven



PostgreSQL is a robust and powerful RDBMS with a lot of features, including namespaces for its objects.
Django is a great web framework that is very robust and has a lot of features as well.
So, you can extract the better of both to achieve better results and to do this, one of the ways is getting a better organization.
Organizing your database objects in namespaces according with its roles will bring benefits for you 😉

About Juliano Atanazio

Brazilian, headbanger and open source enthusiast. My favorite technologies: PostgreSQL, Python, Docker, FreeBSD, and Linux. Not a native english speaker.

Leave a Reply

Your email address will not be published. Required fields are marked *