Simplificando a validação de dados em Python – Real Python

A principal maneira do Pydantic de definir esquemas de dados é por meio de modelos. Um modelo Pydantic é um objeto, semelhante a um modelo Python classe de dadosque define e armazena dados sobre uma entidade com anotada campos. Ao contrário das classes de dados, o foco do Pydantic está centrado na análise, validação e serialização automáticas de dados.

A melhor maneira de entender isso é criar seus próprios modelos, e é isso que o senhor fará a seguir.

Trabalhando com Pydantic BaseModels

Suponha que o senhor esteja criando um aplicativo usado por um departamento de recursos humanos para gerenciar as informações dos funcionários e precise de uma maneira de verificar se as informações dos novos funcionários estão no formato correto. Por exemplo, cada funcionário deve ter um ID, nome, e-mail, data de nascimento, salário, departamento e seleção de benefícios. Esse é um caso de uso perfeito para um modelo Pydantic!

Para definir seu modelo de funcionário, o senhor cria um classe que herda do Pydantic’s BaseModel:

Primeiro, o senhor importa as dependências necessárias para definir seu modelo de funcionário. Em seguida, o senhor cria um enum para representar os diferentes departamentos da sua empresa, e o senhor usará isso para anotar o department em seu modelo de funcionário.

Em seguida, o senhor define seu modelo Pydantic, Employee, que herda de BaseModel e define os nomes e os tipos esperados dos campos de seus funcionários por meio de anotações. Aqui está um detalhamento de cada campo que o senhor definiu em Employee e como o Pydantic o valida quando um Employee é instanciado:

  • employee_id: Este é o UUID do funcionário para o qual o senhor deseja armazenar informações. Ao usar o UUID a Pydantic garante que esse campo seja sempre um UUID válido. Cada instância de Employee receberá um UUID por padrão, conforme especificado pelo senhor ao chamar uuid4().
  • name: O nome do funcionário, que o Pydantic espera que seja uma cadeia de caracteres.
  • email: A Pydantic garantirá que cada funcionário email é válido usando a função email-validator do Python.
  • date_of_birth: A data de nascimento de cada funcionário deve ser uma data válida, conforme anotado pelo date do Python’s datetime do Python. Se o senhor passar uma string para date_of_birth, o Pydantic tentará analisá-la e convertê-la em um date objeto.
  • salary: Este é o salário do funcionário e espera-se que seja um float.
  • department: O departamento de cada funcionário deve ser um dos seguintes HR, SALES, IT, ou ENGINEERING, conforme definido em seu Department enum.
  • elected_benefits: Esse campo armazena se o funcionário escolheu benefícios, e a Pydantic espera que ele seja um booleano.

A maneira mais simples de criar um Employee é instanciá-lo como o senhor faria com qualquer outro objeto Python. Para fazer isso, abra um objeto Python REPL e execute o seguinte código:

Nesse bloco, o senhor importa Employee e cria um objeto com todos os campos de funcionário necessários. O Pydantic valida e coage com êxito os campos que o senhor passou e cria um objeto Employee válido. Observe como o Pydantic converte automaticamente sua string de data em um objeto date e seu objeto IT para seus respectivos Department enum.

Em seguida, veja como o Pydantic responde quando o usuário tenta passar dados inválidos para um Employee instância:

Neste exemplo, o senhor criou uma instância de Employee com campos de dados inválidos. O Pydantic fornece uma mensagem de erro detalhada para cada campo, informando o que era esperado, o que foi recebido e onde o senhor pode ir para saber mais sobre o erro.

Essa validação detalhada é poderosa porque impede que o senhor armazene dados inválidos no Employee. Isso também dá ao senhor a certeza de que o Employee que os objetos instanciados sem erros contêm os dados que o senhor espera e que pode confiar nesses dados no seu código ou em outros aplicativos.

O site da Pydantic BaseModel está equipado com um conjunto de métodos que facilitam a criação de modelos a partir de outros objetos, como dicionários e JSON. Por exemplo, se o senhor quiser instanciar um Employee a partir de um dicionário, o senhor pode usar o comando .model_validate() método de classe:

Aqui, o senhor cria new_employee_dictum dicionário com os campos do funcionário e o passa para .model_validate() para criar um Employee instância. Nos bastidores, o Pydantic valida cada entrada do dicionário para garantir que esteja em conformidade com os dados que o senhor espera. Se algum dos dados for inválido, o Pydantic lançará um erro da mesma forma que o senhor viu anteriormente. O senhor também será notificado se algum campo estiver faltando no dicionário.

O senhor pode fazer a mesma coisa com objetos JSON usando .model_validate_json():

Neste exemplo, new_employee_json é uma cadeia de caracteres JSON válida que armazena os campos do funcionário, e o senhor usa .model_validate_json() para validar e criar um Employee a partir de new_employee_json. Embora possa parecer sutil, a capacidade de criar e validar modelos Pydantic a partir do JSON é poderosa porque o JSON é uma das formas mais populares de transferir dados pela Web. Esse é um dos motivos pelos quais o FastAPI se baseia no Pydantic para criar APIs REST.

O senhor também pode serializar modelos Pydantic como dicionários e JSON:

Aqui, o senhor usa .model_dump() e .model_dump_json() para converter seu new_employee em um dicionário e em uma string JSON, respectivamente. Observe como o .model_dump_json() retorna um objeto JSON com date_of_birth e department armazenados como cadeias de caracteres.

Embora o Pydantic já tenha validado esses campos e convertido seu modelo em JSON, quem usar esse JSON downstream não saberá que date_of_birth precisa ser um date e department precisa ser uma categoria em seu Department enum. Para resolver isso, o senhor pode criar um esquema JSON do seu Employee modelo.

Os esquemas JSON informam ao senhor quais campos são esperados e quais valores são representados em um objeto JSON. O senhor pode pensar nisso como a versão JSON do seu Employee definição de classe. Veja como o senhor gera um esquema JSON para Employee:

Quando o senhor ligar .model_json_schema()o senhor obtém um dicionário que representa o esquema JSON do seu modelo. A primeira entrada que o senhor vê mostra os valores que department podem assumir. O senhor também vê informações sobre como os campos devem ser formatados. Por exemplo, de acordo com este esquema JSON, employee_id deve ser um UUID e date_of_birth deve ser uma data.

O senhor pode converter seu esquema JSON em uma string JSON usando json.dumps(), o que permite que o senhor converta praticamente qualquer linguagem de programação para validar objetos JSON produzidos pelo seu Employee do seu modelo. Em outras palavras, o Pydantic não só pode validar os dados recebidos e serializá-los como JSON, mas também fornece a outras linguagens de programação as informações necessárias para validar os dados do seu modelo por meio de esquemas JSON.

Com isso, o senhor agora entende como usar o BaseModel para validar e serializar seus dados. A seguir, o senhor aprenderá a usar campos para personalizar ainda mais a validação.

Uso de campos para personalização e metadados

Até agora, seu Employee valida o tipo de dados de cada campo e garante que alguns dos campos, como email, date_of_birth, e department, assumem formatos válidos. No entanto, digamos que o senhor também queira garantir que salary seja um número positivo, name não é uma string vazia, e email contenha o nome de domínio de sua empresa. O senhor pode usar a função Field da Pydantic para fazer isso.

O Field permite que o senhor personalize e adicione metadados aos campos do seu modelo. Para ver como isso funciona, dê uma olhada neste exemplo:

Aqui, o senhor importa Field juntamente com as outras dependências que usou anteriormente e atribui valores padrão a algumas das dependências do Employee campos. Aqui está um detalhamento do Field que o senhor usou para adicionar validação e metadados adicionais aos seus campos:

  • default_factory: O senhor usa isso para definir um callable que gera valores padrão. No exemplo acima, o senhor define default_factory para uuid4. Esta chamada uuid4() para gerar um UUID aleatório para o employee_id quando necessário. O senhor também pode usar um lambda para maior flexibilidade.
  • frozen: Esse é um parâmetro booleano que o senhor pode definir para tornar seus campos imutáveis. Isso significa que, quando o frozen é definido como Trueo campo correspondente não poderá ser alterado depois que o modelo for instanciado. Neste exemplo, employee_id, name, e date_of_birth são tornados imutáveis usando o frozen .
  • min_length: O senhor pode controlar o comprimento dos campos de string com min_length e max_length. No exemplo acima, o senhor garante que name tenha pelo menos um caractere de comprimento.
  • pattern: Para campos de string, o senhor pode definir pattern para um regex para corresponder a qualquer padrão que o senhor esteja esperando para esse campo. Por exemplo, quando o senhor usa a expressão regex no exemplo acima para email, o Pydantic garantirá que todos os e-mails terminem com @example.com.
  • alias: O senhor pode usar esse parâmetro quando quiser atribuir um alias aos seus campos. Por exemplo, o senhor pode permitir que date_of_birth seja chamado de birth_date ou salary a ser chamado compensation. O senhor pode usar esses aliases ao instanciar ou serializar um modelo.
  • gt: Esse parâmetro, abreviação de “greater than” (maior que), é usado em campos numéricos para definir valores mínimos. Neste exemplo, a definição de gt=0 garante que o salary seja sempre um número positivo. O Pydantic também tem outros restrições numéricas, tais como lt que é a abreviação de “less than” (menos que).
  • repr: Esse parâmetro booleano determina se um campo é exibido na representação de campo do modelo. Neste exemplo, o senhor não verá date_of_birth ou salary quando o senhor imprimir um Employee instância.

Para ver essa validação extra em ação, observe o que acontece quando o senhor tenta criar um Employee com dados incorretos:

Aqui, o senhor importa o Employee atualizado e tenta validar um dicionário com dados incorretos. Em resposta, o Pydantic apresenta três erros de validação dizendo que o name precisa ter pelo menos um caractere, email deve corresponder ao nome de domínio de sua empresa, e salary deve ser maior que zero.

Agora observe os recursos adicionais que o senhor obtém ao validar corretamente Employee dados:

Neste bloco, o senhor cria um dicionário e um Employee com o .model_validate(). Em employee_data, observe como o senhor usou birth_date em vez de date_of_birth e compensation em vez de salary. O Pydantic reconhece esses aliases e atribui internamente seus valores ao nome de campo correto.

Como o senhor define repr=False, o senhor pode ver que salary e date_of_birth não são exibidos no Employee o senhor precisa acessá-los explicitamente como atributos para ver seus valores. O senhor precisa acessá-los explicitamente como atributos para ver seus valores. Por fim, observe o que acontece quando o senhor tenta alterar um campo congelado:

Aqui, o senhor primeiro altera o valor de department de IT para HR. Isso é perfeitamente aceitável porque department não é um campo congelado. No entanto, quando o senhor tenta alterar name, o Pydantic apresenta um erro dizendo que name é um campo congelado.

Agora o senhor tem uma sólida compreensão do conceito de Pydantic BaseModel e do Field classes. Somente com elas, o senhor pode definir muitas regras de validação e metadados diferentes em seus esquemas de dados, mas às vezes isso não é suficiente. A seguir, o senhor levará sua validação de campo ainda mais longe com os validadores Pydantic.