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:

udtypes.py

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:

models.py

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:

Exemplo 11: Definindo tipos

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:

  1. nome: obrigatório, string
  2. 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:

models.py

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:

Exemplo 12: Pessoa tem cachorrinho

Repare também que é possível importar apenas o modelo Cachorrinho:

Exemplo 13: 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:

  1. Definindo tipos customizados (ver exemplo 11);
  2. 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:

models.py

Assim se nós passarmos qualquer outra linguagem diferente daquelas quatro que definimos vamos ver um erro:

Exemplo 14: Validadores — tipo

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.

detalhe do models.py

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:

Exemplo 15: Validadores — modelo DataError

E conferindo, se passarmos um dado válido a validação passa sem erros:

Exemplo 16: Validadores — modelo

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