Indo além no Schematics: Personalizando nossos modelos e validações
Não basta definir modelos e usar as validações que vem de fábrica, às vezes precisamos expandir essas validações para casos específicos do nosso domínio. Vamos ver como fazer isso nesse tutorial.
Aqui vamos continuar de onde paramos na primeira parte do tutorial: “Introdução ao Schematics: um guia para validar dados de um JSON”. Então estou considerando que você já fez todos os passos e aprendeu com a primeira parte e vamos aprofundar nosso estudo aqui.
Material de apoio
Todos os exemplos e códigos que vou mostrar nesse post estão disponíveis nesse repositório do GitHub para você utilizar como material de apoio para esse post.
Definindo tipos
Além de modelos próprios como o nosso Pessoa
, é possível definir também os tipos customizados e assim criarmos estruturas mais complexas e com validações mais exóticas para o nosso modelo.
Por motivos organizacionais iremos separar os tipos que escreveremos em um novo arquivo chamados udtypes.py
(user defined types). Sendo assim, a nossa estrutura de arquivos nova fica assim:
pessoa/
├── __init__.py
├── models.py
└── udtypes.py
Vamos supor que na nossa tabela de exemplo, o campo idade
tem que ter exatamente dois algarismos. Dessa forma, podemos implementar o tipo chamado SizedNumberType
, que herda de IntType
e confere se o len
daquele número é diferente de 2
e caso seja, levanta um erro de validação:
Repare que, na criação de novos tipos os métodos para validação devem obrigatoriamente começar com prefixo validate_
como é o caso do validate_number
que implementamos acima. Agora para utilizar esse novo tipo, basta importá-lo no nosso arquivo de modelos e definir um campo para utilizá-lo assim:
Agora se passarmos um valor para o campo idade
com uma quantidade de algarismos diferente de 2
, vamos nos deparar com um erro de validação, veja:
Models como campo
Da mesma forma que podemos definir tipos novos com validações próprias podemos também definir modelos auxiliares para serem utilizados como subestruturas nos campos dos nossos modelos.
Vamos transformar nosso modelo inicial em uma tabela que contenha também um campo chamado cachorrinhos
. Nesse campo queremos armazenar uma lista de uma estrutura nova chamada Cachorrinho
. No entanto cada cachorrinho vai ter dois campos:
- nome: obrigatório, string
- cor: obrigatório, string
Sendo assim, ao invés de herdar de um tipo do Schematics, nosso Cachorrinho
vai ser um modelo e portanto herdará de Model
. Como Pessoa
vai conter um campo de Cachorrinho
precisamos colocar as definições do nosso modelo novo antes do modelo Pessoa
e, ao criarmos o campo em cachorrinhos
em Pessoa
vamos precisar importar o ModelType
, pois é esse tipo que permite usar modelos em campos:
Após essas alterações, quando fizermos a importação do nosso modelo Pessoa
no console, conseguiremos passar as informações dos cachorrinhos para nossa instância, veja:
Repare também que é possível importar apenas o modelo Cachorrinho
:
Indo além na validação
Existem dois níveis de validação:
- Validação a nível de tipo: a validação para cada tipo de dado, seja ele definido pelo usuário ou importado do Schematics, é essa validação por exemplo que confere se os valores recebidos nos campos podem ser convertidos para os seus tipos (ver exemplos 2 e 4);
- E a validação a nível de modelo: uma que acontece com base não só nos valores dos campos, mas também nas regras passadas por parâmetro a cada campo como por exemplo a obrigatoriedade.
Temos duas formas de implementar a validação à nível de tipo:
- Definindo tipos customizados (ver exemplo 11);
- Ou implementando validadores.
A validação em tipos customizados nós já vimos, criamos o tipo SizedNumber
com seu método próprio de validação o validate_number
. Mas para o caso de validações para apenas um campo especificamente, podemos usar os validadores. Validadores são métodos definidos no início do modelo e que são passados para o campo de interesse usando o parâmetro validators
.
Por exemplo, vamos supor que para o campo de linguagens eu só posso aceitar valores presentes nessa lista: ["Python", "R", "Go", "Rum"]
. Como essa é uma validação específica do campo linguagens
, podemos fazer algo assim:
Assim se nós passarmos qualquer outra linguagem diferente daquelas quatro que definimos vamos ver um erro:
Então, você deve estar se perguntando: “Se temos dois tipos de validação, qual o melhor tipo de validação a nível de campo?” E nesse caso, é necessário levar em consideração os seus modelos como um todo. Se você precisar passar o mesmo conjunto de validadores para mais de um campo de mesmo tipo, talvez valha à pena implementar um tipo customizado com essa validação pois isso te ajudará a manter o código mais organizado.
E se as validações a nível de tipo não forem o suficiente pro seu modelo ficar no estado ideal, ainda podemos definir validações a nível de modelo. Esses métodos estão associados a um campo e precisam começar seu nome com o prefixo validate_
assim como a validação de campos customizados seguidos do nome do campo à que se destina aquela validação.
Como estão associados a um campo, esses métodos vêm por último na implementação do nosso modelo. Vamos usar o modelo Cachorrinho
e implementar uma validação desse tipo que deve conferir se o campo nome
começa com letra maiúscula e caso não comece, levante um erro. Para isso, vamos definir o método validate_nome
.
Note que validate_nome
recebe como parâmetros self
, data
e value
, qualquer método para servir esse tipo de validação deve ter essa mesma assinatura, sempre recebendo esses três parâmetros. Agora com o nosso modelo Cachorrinho
atualizado, podemos passar um dado que não possui a primeira letra como maiúscula no campo nome
e ver se o Traceback aparece ao chamarmos o método validate
:
E conferindo, se passarmos um dado válido a validação passa sem erros:
Conclusão
Depois de aprender tudo isso, você consegue desenhar estruturas de dados usando Python sem complicações e com isso vai poder trocar dados por aí com muito mais tranquilidade e consistência.
Links
- A primeira parte desse tutorial pode ser encontrada aqui.
- Todos os exemplos e códigos que eu mostrei nesse post estão disponíveis nesse repositório do GitHub.
- Recomendo fortemente a leitura da documentação do Schematics.