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:
psql
[>] Criação do usuário de banco de dados da aplicação:
CREATE 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:
CREATE 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”:
\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:
CREATE SCHEMA ns_rh;
[>] Criação do schema para os metadados do Django:
CREATE SCHEMA ns_django;
[>] Vamos verificar a criação do schema consultando todos schemas que não são catálogos (metadados do PostgreSQL):
SELECT nspname AS namespace FROM pg_catalog.pg_namespace WHERE 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:
CREATE TABLE ns_rh.tb_pessoa( id_ int GENERATED ALWAYS AS IDENTITY, nome text not null, sobrenome text not null, PRIMARY KEY (id_) );
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):
virtualenv -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:
cd django &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:
pip 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:
django-admin startproject my_project
[$] Renomeie o diretório do projeto para src:
mv 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:
cat &amp;amp;lt;&amp;amp;lt; EOF &amp;amp;gt; src/my_project/db.conf DB_HOST = 'localhost' DB_NAME = 'db_test' DB_USER = 'user_test' DB_PASSWORD = '123' DB_PORT = 5432 DB_OPTIONS = '-c search_path=ns_django,public' EOF
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:
vim src/my_project/settings.py
Logo após os imports adicione a seguinte linha:
from configobj import ConfigObj
Modifique a sessão “Database” conforme abaixo:
# Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases # Database configuration file location DB_CONF_FILE = f'{BASE_DIR}/my_project/db.conf' # Read the configurations from file DB_CONFIG = ConfigObj(DB_CONF_FILE) # Database connection parameters DB_HOST = DB_CONFIG['DB_HOST'] DB_NAME = DB_CONFIG['DB_NAME'] DB_USER = DB_CONFIG['DB_USER'] DB_PASSWORD = DB_CONFIG['DB_PASSWORD'] DB_PORT = DB_CONFIG['DB_PORT'] DB_OPTIONS = DB_CONFIG['DB_OPTIONS'] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': DB_NAME, 'USER': DB_USER, 'PASSWORD': DB_PASSWORD, 'HOST': DB_HOST, 'PORT': DB_PORT, 'OPTIONS': {'options': DB_OPTIONS}, } }
[$] Criação de link simbólico para manage.py:
ln -s `pwd`/src/manage.py `pwd`/bin/manage.py
Facilita nosso trabalho adicionando manage.py em nosso $PATH.
[$] Execute o servidor web virtual:
manage.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:
cd src
[$] Vamos verificar a árvore de diretórios de arquivos:
tree .
.
├── 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:
manage.py migrate
[$] Criação do super usuário do Django fornecendo previamente usuário e e-mail:
manage.py createsuperuser \ --username swordmaster \ --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:
manage.py dbshell
[>] Verifique as tabelas que estão dentro do schema ns_rh:
SELECT schemaname AS "Schema / Namespace", relname AS "Tabela" FROM pg_catalog.pg_stat_user_tables WHERE schemaname = 'ns_rh';
Schema / Namespace | Tabela --------------------+----------- ns_rh | tb_pessoa
[>] Verifique as tabelas que estão dentro do schema ns_django:
SELECT schemaname AS "Schema / Namespace", relname AS "Tabela" FROM pg_catalog.pg_stat_user_tables WHERE 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:
\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:
SELECT * 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:
manage.py startapp recursos_humanos
[$] Para ativar essa aplicação temos que habilitá-la no arquivo settings.py:
vim my_project/settings.py
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Custom Apps 'recursos_humanos', ]
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:
mkdir recursos_humanos/models
[$] Remova o arquivo models.py:
rm -f recursos_humanos/models.py
[$] Criação do model:
vim recursos_humanos/models/rh.py
from django.db.models import AutoField from django.db.models import Model from django.db.models import TextField class Pessoa(Model): ''' Model: Pessoa Namespace: ns_rh Table: tb_pessoa ''' id_ = AutoField(db_column='id_', name='id', primary_key=True,) nome = TextField(db_column='nome', name='nome',) sobrenome = TextField(db_column='sobrenome', name='sobrenome',) def __str__(self): return f'{self.nome} {self.sobrenome}' class Meta: db_table = 'ns_rh"."tb_pessoa' # 'schema"."objeto' 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:
echo 'from recursos_humanos.models.rh import Pessoa' &amp;amp;gt; \ recursos_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:
manage.py makemigrations recursos_humanos
[$] Migração falsa:
manage.py migrate --fake recursos_humanos
[$] Vamos verificar a hierarquia de diretórios e arquivos com exceção do cache:
tree 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):
manage.py shell
# import do model Pessoa from recursos_humanos.models.rh import Pessoa
# Criação de um objeto Pessoa p = Pessoa(nome='Ludwig', sobrenome='van Beethoven')
# print no objeto (invocação do método __str__()) print(p)
Ludwig van Beethoven
# Efetivar o registro na base de dados (COMMIT) p.save()
Pressione + D para sair.
[$] Shell da base de dados (psql):
manage.py dbshell
[>] Uma query para verificar se o dado foi inserido pelo Django:
SELECT 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:
About author
Você pode gostar também
Gerando Dados Aleatórios com Paralelização no Shell: Guia Prático
Gerar dados aleatórios com paralelização no shell. Falando assim parece até alguma coisa muito importante ou difícil, mas vamos entender sua utilidade na prática. Vez ou outra preciso de uma
Descubra o poder do CouchDB: o banco de dados NoSQL orientado a documentos
CouchDB é um banco de dados NoSQL orientado a documentos. Utiliza JSON como formato de dados e JavaScript como linguagem de consulta. Diferente da maioria dos outros bancos de dados,
Descubra o Redis: a solução open source para armazenamento de dados
Administradores de sistemas e desenvolvedores provavelmente em algum momento já precisaram de alguma solução para armazenar dados temporários – como token de sessão – que sejam acessíveis de um ponto