Todo serviço Python eventualmente tem que responder à mesma pergunta: o que você faz com dados que chegam de fora do controle do seu programa - um corpo de requisição HTTP, um arquivo de configuração, uma mensagem de uma fila, uma linha de um CSV que alguém carregou?
A resposta do Pydantic é transformar esses dados que chegam em um objeto Python fortemente tipado exatamente uma vez, no ponto em que ele cruza para o seu código, e rejeitar ou converter qualquer coisa que não se encaixe na forma que você declarou.
Pydantic Basics mostra a sintaxe para definir essa forma com BaseModel; esta página é sobre o modelo subjacente - por que a validação acontece onde acontece, como o pydantic-core a torna rápida o suficiente para ser usada em todos os lugares, e por que despejar dados de volta é uma operação genuinamente diferente de descrever sua forma.
Pydantic analisa entrada não confiável e fracamente tipada em um objeto Python fortemente tipado em um único cruzamento de limite, e então trata esse objeto como confiável durante o resto de sua vida útil.
Por que Importa: Sem uma camada de limite, as verificações de correção de tipo ficam espalhadas por todas as funções que tocam dados externos, e "isso é um User válido?" se torna uma pergunta que cada chamador tem que responder novamente em vez de uma garantia que o sistema de tipos já lhe dá.
Conceitos Chave:validação de limite, pydantic-core, esquema principal (core schema), conversão (coercion), modo estrito (strict mode), serialização.
Quando Usar: Análise de corpos de requisição, arquivos de configuração e variáveis de ambiente; validação de linhas vindas de um CSV ou mensagem de fila; construção de objetos de domínio tipados nos quais o código downstream pode confiar sem revalidar.
Limitações / Compromissos: Pydantic valida forma e tipo, não regras de negócios que dependem de outras linhas ou estado externo; a validação ocorre na construção, portanto, mutar um modelo após a criação contorna essas verificações, a menos que você opte explicitamente por participar novamente.
Tópicos Relacionados: dataclasses e attrs, geração de JSON Schema, validadores em nível de campo, gerenciamento de configurações.
A frase "analisar, não validar" descreve a mudança mental que o Pydantic pede que você faça.
Uma função de validação que retorna True ou False informa que os dados eram aceitáveis em um momento, mas devolve o mesmo dict ou str não tipado com o qual você começou, então cada função posterior ainda tem que confiar que a verificação ocorreu corretamente a montante.
Um analisador, em vez disso, converte esses dados em um novo tipo mais específico - um objeto User em vez de um dict - e uma vez que esse objeto existe, sua existência é a prova de que a validação já foi bem-sucedida.
BaseModel é o analisador do Pydantic: você declara a forma de destino com dicas de tipo Python comuns, e instanciar a classe com dados brutos produz uma instância totalmente tipada ou levanta um ValidationError que informa exatamente o que não se encaixou.
Uma analogia útil é um posto de controle de fronteira que também troca moeda.
Os dados não são apenas inspecionados e liberados em sua forma original; eles são convertidos na moeda local (seu modelo tipado) no ponto de cruzamento, para que nada a jusante tenha que continuar perguntando qual moeda está segurando.
O Pydantic 2 reconstruiu este posto de controle com pydantic-core, um motor de validação escrito em Rust, que é por que o mesmo estilo declarativo que já era conveniente no Pydantic 1 se tornou rápido o suficiente para ser usado em todas as requisições em uma API de alto tráfego, em vez de reservá-lo para envios de formulários e carregamento de configurações.
A história de desempenho começa na definição da classe, não na instanciação.
Quando uma subclasse de BaseModel é definida, o Pydantic percorre suas dicas de tipo e configuração de campo e as compila uma vez em um esquema principal (core schema) - uma estrutura de dados que o pydantic-core entende - em vez de reinterpretar suas dicas de tipo do zero toda vez que uma instância é criada.
Cada chamada subsequente de User(**data) reutiliza esse esquema já compilado e faz a verificação real campo a campo dentro da extensão Rust, que é por que validar mil registros com Pydantic 2 custa aproximadamente uma compilação de esquema mais mil validações nativas baratas, não mil inspeções de tipo dinâmicas caras.
class User(BaseModel): id: int email: str# O esquema para User é compilado uma vez, aqui, na definição da classe:User.__pydantic_core_schema__ # a forma compilada contra a qual o pydantic-core valida# Cada instanciação abaixo reutiliza esse esquema - ele não é recompilado por chamada:User(id="1", email="a@example.com") # "1" é convertido para int no modo lax (padrão)
Esse trecho também mostra conversão (coercion), que é o comportamento padrão, "lax": uma string "1" para um campo int é aceita e convertida, porque na prática muitos dados legítimos (campos de formulário, strings de consulta, números JSON que chegaram como texto) são tecnicamente do tipo errado, mas inequivocamente conversíveis.
O modo estrito (strict mode) desliga isso, recusando qualquer coisa que já não seja o tipo declarado, o que importa para limites onde a conversão silenciosa ocultaria um erro real - uma chamada interna de serviço para serviço onde um id str em vez de um id int geralmente sinaliza um erro do chamador, não uma peculiaridade de formatação.
Validadores (field_validator, model_validator) adicionam lógica personalizada sobre o esquema compilado em vez de substituí-lo: um field_validator é executado após o pydantic-core já ter convertido e verificado o tipo desse campo, então ele opera em dados que ele já pode confiar que são do tipo Python correto, não na entrada bruta.
model_dump(), model_dump_json() e model_json_schema() parecem três sabores da mesma funcionalidade, mas respondem a três perguntas não relacionadas, e confundi-las é um dos erros arquitetônicos mais comuns em bases de código com uso intensivo de Pydantic.
model_dump() e model_dump_json() são serialização: eles pegam os dados de uma instância real e produzem um dict Python ou uma string JSON a partir dela, e o Pydantic executa isso através de seu próprio esquema de serialização compilado, que é por que a lógica personalizada de field_serializer e opções como exclude_none ou by_alias afetam apenas essas chamadas.
model_json_schema() nunca olha para uma instância; ele inspeciona a definição da classe e produz um documento JSON Schema descrevendo como seria uma instância válida, que é a forma que o FastAPI publica como OpenAPI e a forma que uma ferramenta de geração de código cliente consome - é uma descrição estática, não uma transformação de dados.
A consequência prática é que alterar um field_serializer muda o que model_dump_json() produz, mas nunca muda model_json_schema(), e vice-versa - uma Field(description=...) em nível de esquema aparece no JSON Schema, mas não tem efeito em um dump.
Onde a validação deve residir é uma decisão arquitetônica relacionada e mais consequente: empurrar todas as verificações para o modelo no limite HTTP não é o mesmo que empurrar todas as verificações apenas para lá.
Localização da validação
Força
Fraqueza
Melhor Encaixe
Limite do framework (modelos de requisição FastAPI)
Rejeita entrada malformada antes que qualquer código de handler seja executado; documentação OpenAPI gratuita
Vê apenas uma requisição isoladamente; não pode verificar regras que abrangem múltiplos recursos
Forma, tipo e regras de negócios de campo único em cada requisição de entrada
Validadores em nível de modelo (model_validator)
Regras entre campos permanecem próximas aos campos que elas restringem
Ainda limitado ao que está dentro dessa única instância do modelo
Regras como "data de término deve ser após a data de início"
Verificações na camada de serviço
Pode consultar outros dados (unicidade, propriedade, cotas)
Fácil de pular se um método de serviço for chamado de múltiplos pontos de entrada
Regras que precisam de consulta ao banco de dados ou estado de outro serviço
Restrições de banco de dados
Impostas mesmo que o código da aplicação tenha um bug
Erros surgem como exceções genéricas de DB, não como ValidationErrors amigáveis
Último recurso para garantias de unicidade, chaves estrangeiras e não nulas
Nenhuma linha substitui as outras - uma restrição de e-mail único no banco de dados é o último recurso para uma verificação que já deveria ter ocorrido, mais rápido e com uma mensagem de erro melhor, na camada do modelo ou de serviço.
O papel do Pydantic se estreita à medida que você desce nessa tabela: ele é responsável pela forma e tipo na borda, e não é a camada responsável por "este e-mail já existe" ou "este usuário tem permissão para atualizar este registro".
"A validação Pydantic significa que meus dados estão corretos de acordo com as regras de negócios." Pydantic confirma que um campo é um int dentro de quaisquer restrições declaradas; ele não tem ideia se esse inteiro é um saldo de conta válido para este usuário específico, o que é uma preocupação da camada de serviço ou de banco de dados.
"model_dump() e o antigo .dict() fazem a mesma coisa.".dict() é um shim de compatibilidade do Pydantic 1; model_dump() é o caminho nativo v2 que respeita serializadores personalizados e a distinção mode="json", e misturar os dois em uma base de código produz resultados sutilmente inconsistentes.
"model_json_schema() reflete o comportamento de serialização em tempo de execução." Ele reflete apenas a forma declarada da classe; um field_serializer que altera como um dump se parece não tem efeito no JSON Schema gerado, porque a geração de esquema nunca executa uma instância através da serialização.
"Uma vez que um modelo é construído, seus campos permanecem válidos." A validação ocorre no momento do __init__; atribuir um novo valor a um atributo de uma instância existente contorna completamente essa verificação, a menos que model_config = ConfigDict(validate_assignment=True) seja definido.
"O modo estrito é o que você deveria usar em todos os lugares para segurança." O modo estrito rejeita dados legítimos e comumente recebidos, como strings numéricas de parâmetros de consulta; a conversão lax (o padrão) é o que torna o Pydantic utilizável nas bordas HTTP em primeiro lugar, e o modo estrito é para limites internos mais estreitos e já tipados.
O que "analisar, não validar" realmente significa no contexto do Pydantic?
Significa que o Pydantic não apenas retorna True/False sobre se os dados são aceitáveis - ele converte esses dados em um novo objeto tipado.
Uma vez que uma instância User existe, sua existência é a prova de que a validação já passou, então os chamadores não precisam revalidá-la.
Por que o Pydantic 2 é tão mais rápido que o Pydantic 1?
O Pydantic 2 moveu o trabalho real de validação para o pydantic-core, uma extensão Rust, em vez de fazê-lo em Python puro. A API voltada para Python (BaseModel, dicas de tipo) permaneceu familiar, mas o esquema é compilado uma vez e executado nativamente em cada instanciação.
O que é um "esquema principal (core schema)" e quando ele é construído?
É a representação interna compilada da forma de um modelo contra a qual o pydantic-core realmente valida. Ele é construído uma vez, quando a subclasse BaseModel é definida - não é rederivado de suas dicas de tipo a cada chamada.
Qual é a diferença entre modo lax e modo estrito?
Modo lax (o padrão) converte tipos compatíveis, como uma string numérica em um int.
Modo estrito rejeita qualquer coisa que já não seja o tipo exato declarado.
O modo lax se encaixa em limites fracamente tipados como strings de consulta HTTP; o modo estrito se encaixa em limites internos já tipados.
Como `model_dump()` é diferente de `model_json_schema()`?
model_dump() pega os dados de uma instância real e os serializa para um dict ou JSON. model_json_schema() nunca toca em uma instância - ele inspeciona a definição da classe e produz um JSON Schema descrevendo a forma válida. Alterar um serializador afeta o primeiro, nunca o segundo.
Os validadores de campo são executados antes ou depois da conversão de tipo?
Depois. Um field_validator recebe dados que o pydantic-core já converteu e confirmou que correspondem ao tipo declarado, então o código do validador personalizado pode assumir que está trabalhando com o tipo Python correto.
O Pydantic continua validando um modelo após sua criação?
Não, por padrão. A validação ocorre no momento da construção; mutar um atributo posteriormente pula essa verificação completamente, a menos que o modelo defina model_config = ConfigDict(validate_assignment=True).
Onde devem viver as regras de negócios que precisam de consulta ao banco de dados?
Não dentro do modelo Pydantic - ele não tem acesso a uma sessão de banco de dados. Essas pertencem a uma camada de serviço que pode consultar coisas como unicidade ou propriedade, com o próprio banco de dados como um último recurso de restrição.
O Pydantic é útil apenas para corpos de requisição FastAPI?
Não - é usado com a mesma frequência para carregamento de configurações/ajustes, análise de mensagens de fila, validação de linhas de um arquivo e definição de objetos de domínio tipados passados entre serviços internos, independentemente de qualquer framework web.
Por que o Pydantic rejeita campos desconhecidos por padrão em algumas configurações?
Rejeitar chaves inesperadas (extra="forbid") pega erros de digitação e formas de payload inesperadas precocemente, na borda, em vez de ignorar silenciosamente dados que o chamador pensava que estavam sendo usados.
Dois pedaços de código diferentes podem obter resultados inconsistentes do mesmo modelo?
Sim, se um usar o método de compatibilidade legado .dict() e outro usar model_dump() - eles podem divergir em como serializadores personalizados e conversões específicas de JSON são aplicados. Padronizar em model_dump()/model_dump_json() evita essa deriva.
Qual é a limitação honesta do modelo de validação de limite?
Ele só sabe o que está dentro do payload que está sendo validado - ele não pode verificar coisas como unicidade contra outras linhas ou o estado de um segundo recurso, então ele tem que passar para uma camada de serviço ou restrição de banco de dados para qualquer coisa que não seja autocontida.