Organize seus objetos de banco de dados com schemas PostgreSQL no Django

Organize seus objetos de banco de dados com schemas PostgreSQL no Django

Que tal se você pudesse organizar seus objetos de bancos de dados (suas tabelas, views, functions, procedures etc.) em namespaces de acordo com suas respectivas funções no sistema?
Neste artigo veremos a maneira correta de lidar com schemas PostgreSQL no Django e algumas pequenas dicas sobre models do Django e Python.

Schema

Também conhecido com namespace, o schema é um tipo de objeto de banco de dados cujo propósito é ser uma camada de organização hierárquica que está logo abaixo de uma base de dados.

No PostgreSQL, “public” é o schema padrão, mas você pode criar seus próprios namespaces para organizar outros tipos de objetos como tabelas, views, functions, procedures etc.

Hierarquia de objetos de bancos de dados

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

Nosso laboratório

  • Python 3.9
  • Django 3.1
  • PostgreSQL 13

O funcionamento deve ser parecido em versões anteriores.

Códigos

  • [>] SQL e meta comandos psql;
  • [$] Shell bash (Linux, FreeBSD, Unix*)

PostgreSQL

A estrutura da base de dados será a primeira coisa que faremos.

  • Criação do usuário de banco de dados da aplicação;
  • Criação da base de dados;
  • Criação do schema;
  • Criação da tabela.

Vamos criar nosso próprio exemplo na ferramenta built-in em linha de comando, o psql:

 

[>] Criação do usuário de banco de dados da aplicação:

1CREATE ROLE user_test ENCRYPTED PASSWORD '123' LOGIN;

O role de bases de dados foi criado com senha encriptada (isso é padrão, apenas foi explicitado) e o atributo LOGIN, o que o torna um usuário.

 

[>] Criação da bases de dados para os testes:

1CREATE DATABASE db_test OWNER user_test;

A base de dados pertence a ao usuário “user_test”.

 

[>] Conecte ao banco de dados com o usuário “user_test”:

1\c db_test user_test

“\c” é um meta-comando do psql para conectar a uma base de dados.

 

[>] Criação de um schema para o RH:

1CREATE SCHEMA ns_rh;

 

[>] Criação do schema para os metadados do Django:

1CREATE SCHEMA ns_django;

 

[>] Vamos verificar a criação do schema consultando todos schemas que não são catálogos (metadados do PostgreSQL):

1SELECT
2nspname AS namespace
3FROM pg_catalog.pg_namespace
4WHERE nspname !~ '(^pg_|information_schema)';
namespace 
-----------
public
ns_rh

 

Note que é exibido o namespace padrão (public) e o namespace ns_rh, criado para nosso laboratório.

[>] Criação de uma tabela no schema ns_rh:

1CREATE TABLE ns_rh.tb_pessoa(
2id_ int GENERATED ALWAYS AS IDENTITY,
3nome text not null,
4sobrenome text not null,
5PRIMARY KEY (id_)
6);

Uma simples tabela…

Aperte <Ctrl> + D para sair.

 

Django

Vamos criar a codificação em Python:

  • Ambiente virtual;
  • Instalação de módulos Python;
  • Criação de projeto Django e sua configuração;
  • Criação de app Django;
  • Criação do model Django;
  • Migrations;
  • Testes em shell.

 

[$] Criação do ambiente virtual (virtualenv):

1virtualenv -p `which python3.9` django

O caminho absoluto do binário Python 3.8 foi indicado como interpretador Python desse ambiente.

 

[$] Acesse o diretório do ambiente e ative-o:

1cd django &amp;amp;amp;amp;&amp;amp;amp;amp; source bin/activate

Seu prompt mudou, iniciando por “(django)” indicando que seu ambiente virtual foi ativado.

 

[$] Instalação dos módulos necessários para os testes:

1pip install django psycopg2-binary configobj ipython

Respectivamente: Django web framework, driver PostgreSQL, leitor de arquivos de configuração e o shell interativo ipython (que oferece mais recursos que o shell padrão).

 

[$] Criação de um novo projeto Django:

1django-admin startproject my_project

[$] Renomeie o diretório do projeto para src:

1mv my_project src

Isso é feito para facilitar a hierarquia de diretórios e não afetará os resultados. Por ser um único projeto, ter um diretório de mesmo nome pode causar uma certa confusão…

 

[$] Usando o recurso de shell heredoc para criação do arquivo de configuração de base de dados:

1cat &amp;amp;amp;lt;&amp;amp;amp;lt; EOF &amp;amp;amp;gt; src/my_project/db.conf
2DB_HOST = 'localhost'
3DB_NAME = 'db_test'
4DB_USER = 'user_test'
5DB_PASSWORD = '123'
6DB_PORT = 5432
7DB_OPTIONS = '-c search_path=ns_django,public'
8EOF

Criamos um arquivo de configuração para conexão com o banco de dados. Observe que o parâmetro “DB_OPTIONS” foi definido como “default search path”, ou seja; prioridade de namespace e adicionamos “ns_django”, que é o que havíamos criado como schema para os metadados do Django.

 

[$] Edite o principal arquivo de configuração do projeto:

1vim src/my_project/settings.py

 

Logo após os imports adicione a seguinte linha:

1from configobj import ConfigObj

 

Modifique a sessão “Database” conforme abaixo:

01# Database
03 
04# Database configuration file location
05DB_CONF_FILE = f'{BASE_DIR}/my_project/db.conf'
06 
07# Read the configurations from file
08DB_CONFIG = ConfigObj(DB_CONF_FILE)
09 
10# Database connection parameters
11 
12DB_HOST = DB_CONFIG['DB_HOST']
13DB_NAME = DB_CONFIG['DB_NAME']
14DB_USER = DB_CONFIG['DB_USER']
15DB_PASSWORD = DB_CONFIG['DB_PASSWORD']
16DB_PORT = DB_CONFIG['DB_PORT']
17DB_OPTIONS = DB_CONFIG['DB_OPTIONS']
18 
19DATABASES = {
20             'default': {
21                         'ENGINE': 'django.db.backends.postgresql',
22                         'NAME': DB_NAME,
23                         'USER': DB_USER,
24                         'PASSWORD': DB_PASSWORD,
25                         'HOST': DB_HOST,
26                         'PORT': DB_PORT,
27                         'OPTIONS': {'options': DB_OPTIONS},
28                         }
29            }

 

[$] Criação de link simbólico para manage.py:

1ln -s `pwd`/src/manage.py `pwd`/bin/manage.py

Facilita nosso trabalho adicionando manage.py em nosso $PATH.

 

[$] Execute o servidor web virtual:

1manage.py runserver 0.0.0.0:8000

 

Teste em seu navegador acessando o endereço: http://localhost:8000 e para interromper dê <Ctrl> + C.

 

[$] Acesse o diretório do projeto:

 

[$] Vamos verificar a árvore de diretórios de arquivos:

.
├── manage.py
└── my_project
├── asgi.py
├── db.conf
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-39.pyc
│   ├── settings.cpython-39.pyc
│   ├── urls.cpython-39.pyc
│   └── wsgi.cpython-39.pyc
├── settings.py
├── urls.py
└── wsgi.py

 

[$] Primeira migração para criar a estrutura de metadados Django e dados iniciais:

1manage.py migrate

 

[$] Criação do super usuário do Django fornecendo previamente usuário e e-mail:

1manage.py createsuperuser \
2--username swordmaster \
3--email swordmaster@localhost

Uma senha tem que ser fornecida e confirmada para a criação do usuário.

 

[$] Entrar no shell interativo de banco de dados:

1manage.py dbshell

 

[>] Verifique as tabelas que estão dentro do schema ns_rh:

1SELECT
2schemaname AS "Schema / Namespace",
3relname AS "Tabela"
4FROM pg_catalog.pg_stat_user_tables
5WHERE schemaname = 'ns_rh';
 Schema / Namespace |  Tabela   
--------------------+-----------
 ns_rh              | tb_pessoa

 

[>] Verifique as tabelas que estão dentro do schema ns_django:

1SELECT
2schemaname AS "Schema / Namespace",
3relname AS "Tabela"
4FROM pg_catalog.pg_stat_user_tables
5WHERE schemaname = 'ns_django';
 Schema / Namespace |           Tabela           
--------------------+----------------------------
 ns_django          | django_migrations
 ns_django          | auth_group_permissions
 ns_django          | auth_group
 ns_django          | django_session
 ns_django          | django_admin_log
 ns_django          | auth_permission
 ns_django          | auth_user_user_permissions
 ns_django          | auth_user_groups
 ns_django          | auth_user
 ns_django          | django_content_type

 

[>] Opcionalmente você também pode fazer essa verificação usando meta-comandos do psql, que terá um resultado um pouco diferente:

1\dt ns_django.*
                     List of relations
  Schema   |            Name            | Type  |   Owner   
-----------+----------------------------+-------+-----------
 ns_django | auth_group                 | table | user_test
 ns_django | auth_group_permissions     | table | user_test
 ns_django | auth_permission            | table | user_test
 ns_django | auth_user                  | table | user_test
 ns_django | auth_user_groups           | table | user_test
 ns_django | auth_user_user_permissions | table | user_test
 ns_django | django_admin_log           | table | user_test
 ns_django | django_content_type        | table | user_test
 ns_django | django_migrations          | table | user_test
 ns_django | django_session             | table | user_test

 

[>] Tabela de informações de usuários do Django:

1SELECT * FROM ns_django.auth_user;
id           | 1
password     | pbkdf2_sha256$216000$NjUtsMG9VVrp$CxWhoQS8ixR9wH8MvyKirc17j6gZHw2nD0Cle7kiYow=
last_login   | 
is_superuser | t
username     | swordmaster
first_name   | 
last_name    | 
email        | swordmaster@localhost
is_staff     | t
is_active    | t
date_joined  | 2020-11-01 15:05:35.724774+00

Por padrão o dbshell, no caso do PostgreSQL utiliza o psql, que é seu utilitário padrão de linha de comando.
Pudemos ver resultados produzidos pelo arquiovo manage.py dentro da base de dados.
Aperte <Ctrl> + D para sair.

 

[$] Criação de uma app Django para o RH:

1manage.py startapp recursos_humanos

 

[$] Para ativar essa aplicação temos que habilitá-la no arquivo settings.py:

1vim my_project/settings.py
01# Application definition
02 
03INSTALLED_APPS = [
04    'django.contrib.admin',
05    'django.contrib.auth',
06    'django.contrib.contenttypes',
07    'django.contrib.sessions',
08    'django.contrib.messages',
09    'django.contrib.staticfiles',
10 
11    # Custom Apps
12    'recursos_humanos',
13]

 

Uma dica Django muito útil: você pode usar um diretório “models” em vez de um arquivo “models.py”. Mas para isso é necessário criar um dunder init file (__init__.py) dentro dele.
Com um diretório você pode colocar vários arquivos dentro dele, cada um de um model diferente, facilitando assim a organização e manutenção.

[$] Criação do diretório “models” dentro do diretório da aplicação:

1mkdir recursos_humanos/models

 

[$] Remova o arquivo models.py:

1rm -f recursos_humanos/models.py

 

[$] Criação do model:

1vim recursos_humanos/models/rh.py
01from django.db.models import AutoField
02from django.db.models import Model
03from django.db.models import TextField
04 
05 
06class Pessoa(Model):
07    '''
08    Model: Pessoa
09 
10    Namespace: ns_rh
11    Table: tb_pessoa
12    '''
13 
14    id_ = AutoField(db_column='id_', name='id', primary_key=True,)
15    nome = TextField(db_column='nome', name='nome',)
16    sobrenome = TextField(db_column='sobrenome', name='sobrenome',)
17 
18    def __str__(self):
19        return f'{self.nome} {self.sobrenome}'
20 
21    class Meta:
22        db_table = 'ns_rh"."tb_pessoa'  # 'schema"."objeto'
23        verbose_name_plural = 'Pessoa'

Para usufruir dos benefícios de schemas PostgreSQL, dentro de seu model, na classe classe interna Meta para o valor do atributo “db_table” é preciso
colocar um ponto envolto por aspas entre o nome do schema e o nome do objeto.

‘schema”.”objeto’

O objeto pode ser uma tabela, view, function, procedure, etc…

 

[$] Dunder init dentro do diretório models para migrações terem efeito:

1echo 'from recursos_humanos.models.rh import Pessoa' &amp;amp;amp;gt; \
2recursos_humanos/models/__init__.py

Isso é necessário para o diretório models funcionar como o arquivo models.py.

 

(Sem) Migrations: meu banco de dados, minhas regras!

Criamos a estrutura de nossa base de dados e nenhum ORM deve fazer isso por nós!
Nós temos o poder!
Nós temos a força!
Nós estamos no comando!

Nossa base de dados, nossas regras!

Modele seu banco de dados com suas próprias regras e faça uma falsa migração Django, porque apenas nós sabemos como os objetos do banco de dados devem ser criados.

 

[$] Crie as migrations para a aplicação recursos_humanos:

1manage.py makemigrations recursos_humanos

 

[$] Migração falsa:

1manage.py migrate --fake recursos_humanos

 

[$] Vamos verificar a hierarquia de diretórios e arquivos com exceção do cache:

1tree recursos_humanos/ | egrep -v '__pycache__|\.pyc'
recursos_humanos/
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│   ├── 0001_initial.py
│   ├── __init__.py
├── models
│   ├── __init__.py
│   └── rh.py
├── tests.py
└── views.py

 

[$] Django Shell (Ipython):

1manage.py shell

 

1# import do model Pessoa
2from recursos_humanos.models.rh import Pessoa

 

1# Criação de um objeto Pessoa
2p = Pessoa(nome='Ludwig', sobrenome='van Beethoven')

 

1# print no objeto (invocação do método __str__())
2print(p)
Ludwig van Beethoven
1# Efetivar o registro na base de dados (COMMIT)
2p.save()

Pressione + D para sair.

 

[$] Shell da base de dados (psql):

1manage.py dbshell

 

[>] Uma query para verificar se o dado foi inserido pelo Django:

1SELECT id_, nome, sobrenome FROM ns_rh.tb_pessoa;
 id |  name  |    surname    
----+--------+---------------
  1 | Ludwig | van Beethoven

Conclusão

O PostgreSQL é um robusto e poderoso SGBD com muitos recursos, incluindo namespaces para seus objetos. O Django é um grande framework web que é muito robusto e tem muitos recursos também. Então, pode-se extrair o melhor de ambos para alcançar melhores resultados e para isso, também devemos conseguir uma melhor organização. Organizando seus objetos de bancos de dados em namespaces de acordo com seus papéis e separando models do Django em arquivos, você só terá benefícios.

 

Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:

CURSOSCONSULTORIA

Anterior Descubra como otimizar seu cluster Kubernetes com o plugin de rede Calico
Próxima Resolvendo a Escassez de Profissionais de TI: Palestra Online na Latinoware 2020

About author

Juliano Atanazio
Juliano Atanazio 2 posts

Graduado em "Informática para Gestão de Negócios" pela FATEC Zona Sul. Atua com Linux e Software Livre desde 2006. Hoje atua principalmente como DBA PostgreSQL e programador Python.

View all posts by this author →

Você pode gostar também

Desenvolvimento

Melhore sua produtividade com dicas avançadas do editor VIM

Intro Continuando a série de posts sobre o VIM, irei tratar aqui sobre o uso do vim no dia a dia, em especial para desenvolvedores. Os principais editores de texto

Desenvolvimento

Curso Python Fundamentals: Sua porta de entrada para o mundo Python

Com foco em atrair diversos perfis e atender à uma demanda crescente por profissionais que conheçam a  linguagem, o curso foi remodelado para ser a sua porta de entrada para

Banco de Dados

Acesso a dados SQL Server através do PostgreSQL: um guia prático

Tenho um PostgreSQL e preciso acessar dados que estão no SQL Server! E agora?! Não! Não precisa entrar em pânico! Existe uma solução para isso. Digamos que em um determinado