Schemas e Namespaces PostgreSQL com Django

Schemas e Namespaces PostgreSQL com 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;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;amp;lt;&amp;amp;amp;lt; EOF &amp;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;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:

CURSOSCONSULTORIA

Anterior Rede Kubernetes com cluster híbrido - parte 1
Próxima 4linux apresentará palestra no Latinoware 2020 falando sobre a escassez de profissionais de TI.

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

API RESTful com Laravel – Parte 2

Neste segundo post, iremos ver a nossa API Laravel funcional e mostrar na tela os nossos resultados. Iremos ver como o Laravel torna simples a criação de Rotas e Controller,

Desenvolvimento

Colações não determinísticas no PostgreSQL

Com o lançamento recente do PostgreSQL 13 e com a grande maturidade das versões anteriores, algumas das novidades dessas versões mais recentes se tornam cada vez mais disponíveis para uso

Desenvolvimento

Curso PHP Desenvolvimento Web Presencial

A Internet tornou-se algo tão natural como cumprimentar as pessoas no nosso cotidiano, e esse é um motivo pelo qual as pessoas devem se interessar em conhecer os aspectos técnicos