quot;, description="CEP no formato XXXXX ou XXXXX-XXX") # 2. Definindo o modelo principal para o usuário class User(BaseModel): id: int = Field(..., gt=0, description="ID único do usuário, deve ser maior que 0") name: str = Field(..., min_length=2, max_length=50, description="Nome completo do usuário") email: EmailStr = Field(..., description="Endereço de e-mail válido") age: int = Field(default=18, ge=0, le=120, description="Idade do usuário, entre 0 e 120") is_active: bool = Field(default=True, description="Indica se o usuário está ativo") signup_date: date = Field(default_factory=date.today, description="Data de cadastro do usuário") tags: Optional[List[str]] = Field(None, description="Lista opcional de tags associadas ao usuário") address: Address = Field(..., description="Endereço do usuário") # 3. Exemplo de um validador personalizado para o nome @validator('name') def name_must_be_capitalized(cls, v): if not v[0].isupper(): raise ValueError('O nome deve começar com uma letra maiúscula') return v class Config: # Permite que o modelo seja instanciado com dados que não correspondem exatamente aos tipos, # tentando fazer a coerção (ex: "123" para int) orm_mode = True # Gera um esquema JSON com descrições schema_vextra = True # --- Exemplos de Uso --- print("--- Exemplo 1: Dados Válidos ---") try: user_data_valid = { "id": 123, "name": "Alice Smith", "email": "[email protected]", "age": 30, "is_active": True, "address": { "street": "Rua das Flores, 100", "city": "São Paulo", "zip_code": "01000-000" } } user1 = User(**user_data_valid) print("Usuário válido criado com sucesso:") print(user1.json(indent=2)) # Converte o modelo para JSON print(f"Tipo do ID: {type(user1.id)}") print(f"Tipo da data de cadastro: {type(user1.signup_date)}") except ValidationError as e: print("Erro de validação:", e.json()) print("\n--- Exemplo 2: Dados Inválidos ---") try: user_data_invalid = { "id": -5, # Inválido: gt=0 "name": "bob", # Inválido: não começa com maiúscula "email": "bob.example.com", # Inválido: não é um email válido "age": 150, # Inválido: le=120 "address": { "street": "Av. Principal", "city": "Rio", "zip_code": "123" # Inválido: regex não corresponde } } user2 = User(**user_data_invalid) print("Usuário inválido criado com sucesso:", user2) except ValidationError as e: print("Erro de validação (dados inválidos):") print(e.json(indent=2)) print("\n--- Exemplo 3: Coerção de Tipos e Valores Padrão ---") try: user_data_coercion = { "id": "456", # Pydantic vai converter para int "name": "Carlos Silva", "email": "[email protected]", # 'age' e 'is_active' serão usados com seus valores padrão (18 e True) "address": { "street": "Rua da Paz, 50", "city": "Belo Horizonte", "zip_code": "30000-000" } } user3 = User(**user_data_coercion) print("Usuário com coerção e valores padrão:") print(user3.json(indent=2)) print(f"ID (originalmente string): {user3.id}, Tipo: {type(user3.id)}") print(f"Idade (valor padrão): {user3.age}") except ValidationError as e: print("Erro de validação:", e.json()) print("\n--- Exemplo 4: Acessando dados do modelo ---") if 'user1' in locals(): print(f"Nome do usuário 1: {user1.name}") print(f"Cidade do usuário 1: {user1.address.city}") print(f"Data de cadastro do usuário 1: {user1.signup_date}") ``` --- # Explicação do Código: 1. **`BaseModel`**: A classe `User` (e `Address`) herda de `pydantic.BaseModel`. Esta é a base para todos os modelos Pydantic. 2. **Type Hints**: Cada campo (`id`, `name`, `email`, etc.) tem um _type hint_ (ex: `int`, `str`, `EmailStr`, `date`, `bool`, `Address`). A Pydantic usa esses _hints_ para validar os tipos de dados. 3. **`Field`**: A função `pydantic.Field` permite adicionar metadados e validações extras aos campos: * `…`: Indica que o campo é **obrigatório** e não tem um valor padrão. * `gt=0`, `min_length=2`, `le=120`: Restrições de valor (maior que, comprimento mínimo, menor ou igual a). * `regex=r"^\d{5}(-\d{3})?quot;`: Validação de formato usando expressões regulares para o CEP. * `description`: Uma descrição que pode ser usada para gerar documentação (ex: OpenAPI/Swagger). * `default=…`: Define um valor padrão para o campo se ele não for fornecido. * `default_factory=date.today`: Define uma função que será chamada para gerar um valor padrão (útil para objetos mutáveis ou datas). 4. **`EmailStr`**: Um tipo especial da Pydantic que valida automaticamente se a string fornecida é um endereço de e-mail válido. 5. **`Optional` e `List`**: Usados do módulo `typing` para indicar que `tags` é uma lista opcional de strings. 6. **Modelos Aninhados**: A classe `Address` é um `BaseModel` separado e é usada como tipo para o campo `address` dentro do modelo `User`, permitindo validação de estruturas de dados complexas. 7. **`@validator`**: Decorador para criar validadores personalizados. No exemplo, `name_must_be_capitalized` garante que o nome comece com uma letra maiúscula. Se a validação falhar, ele levanta um `ValueError`. 8. **`ValidationError`**: Quando os dados fornecidos não correspondem às regras de validação do modelo, a Pydantic lança uma `ValidationError`, que contém detalhes sobre quais campos falharam e por quê. 9. **Coerção de Tipos**: A Pydantic tenta converter os dados para o tipo esperado. Por exemplo, se você passar `"123"` para um campo `int`, ela tentará convertê-lo. 10. **`json()`**: Um método disponível nos modelos Pydantic para serializar a instância do modelo para uma string JSON. 11. **`Config`**: A classe `Config` aninhada permite configurar o comportamento do modelo, como `orm_mode = True` (útil para integrar com ORMs) ou `schema_extra = True` (para incluir descrições no esquema JSON). Este exemplo demonstra como a Pydantic simplifica a validação de dados, tornando seu código mais robusto e menos propenso a erros de tipo ou formato.