> ## Documentation Index
> Fetch the complete documentation index at: https://fastapi2day.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Pydantic Validation and Fields: A Constraints Guide

> Use Field constraints like min_length, ge, and pattern plus custom validators to control exactly what data your Pydantic models accept.

Type checking alone is only the beginning. In real applications you almost always need rules that go beyond "is this an integer?" — an age must be positive, a username must be between three and twenty characters, a discount must fall between 0 and 1. Pydantic's `Field()` function lets you attach these constraints directly to your field declarations, keeping your validation logic co-located with your data definitions. When a value violates a constraint, Pydantic raises a `ValidationError` immediately, just as it does for type mismatches, and gives you a precise message pointing to the offending field.

## The `Field()` function

Import `Field` from `pydantic` and use it as the default value for any field that needs constraints:

```python theme={null}
from pydantic import BaseModel, Field

class User(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    age: int  = Field(gt=0, le=120)
    email: str

user = User(name="Alice", age=30, email="alice@example.com")
print(user.model_dump())
# {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
```

`name` must be between 1 and 100 characters; `age` must be greater than 0 and at most 120. Any violation raises a `ValidationError` the moment you instantiate the model.

## String constraints

Control the length and format of string fields:

```python theme={null}
from pydantic import BaseModel, Field

class UserProfile(BaseModel):
    username: str = Field(min_length=3, max_length=20)
    bio: str      = Field(max_length=500)
    website: str  = Field(pattern=r"^https?://.*")

# Valid
profile = UserProfile(
    username="alice_dev",
    bio="Python developer",
    website="https://alice.dev",
)

# Invalid — username too short
profile = UserProfile(username="ab", bio="Hi", website="https://x.com")
# ValidationError: String should have at least 3 characters
```

| Constraint   | Meaning                                 |
| ------------ | --------------------------------------- |
| `min_length` | Minimum number of characters            |
| `max_length` | Maximum number of characters            |
| `pattern`    | Regular expression the value must match |

## Numeric constraints

Control the acceptable range of numeric fields:

```python theme={null}
from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str
    price: float    = Field(gt=0)            # Must be > 0
    quantity: int   = Field(ge=0)            # Must be >= 0
    discount: float = Field(ge=0.0, le=1.0)  # Must be in [0, 1]

product = Product(name="Widget", price=29.99, quantity=100, discount=0.15)
print(product.model_dump())
# {'name': 'Widget', 'price': 29.99, 'quantity': 100, 'discount': 0.15}
```

| Constraint | Meaning                  |
| ---------- | ------------------------ |
| `gt`       | Greater than             |
| `ge`       | Greater than or equal to |
| `lt`       | Less than                |
| `le`       | Less than or equal to    |

## Default values with `Field()`

You can combine a default value with constraints in a single `Field()` call:

```python theme={null}
from pydantic import BaseModel, Field

class APIConfig(BaseModel):
    api_key: str
    model: str       = Field(default="gpt-4")
    max_tokens: int  = Field(default=1000, ge=1, le=4096)
    temperature: float = Field(default=0.7, ge=0.0, le=2.0)

config = APIConfig(api_key="sk-abc123")

print(config.model)        # gpt-4
print(config.max_tokens)   # 1000
print(config.temperature)  # 0.7
```

## Common `Field()` usage patterns

| Syntax                    | Meaning                                       |
| ------------------------- | --------------------------------------------- |
| `Field(...)`              | Required — the caller **must** supply a value |
| `Field(None)`             | Optional — defaults to `None` if omitted      |
| `Field(default=value)`    | Uses `value` when no input is provided        |
| `Field(alias="raw_name")` | Accepts a different key name in the input     |

```python theme={null}
from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str   = Field(..., min_length=2, max_length=50)   # Required
    price: float = Field(..., gt=0)                          # Required
    stock: int  = Field(default=0, ge=0)                    # Optional
    category: str | None = Field(None)                      # Optional, None default
```

## Adding descriptions

Descriptions appear in generated JSON Schemas and are shown in FastAPI's automatic docs:

```python theme={null}
from pydantic import BaseModel, Field

class Order(BaseModel):
    order_id: str   = Field(description="Unique order identifier")
    total: float    = Field(gt=0, description="Order total in USD")
    items: int      = Field(ge=1, description="Number of items in the order")
```

## Custom validators

Built-in constraints handle most cases, but sometimes you need business logic. Use the `@field_validator` decorator:

```python theme={null}
from pydantic import BaseModel, field_validator

class User(BaseModel):
    username: str

    @field_validator("username")
    @classmethod
    def validate_username(cls, v: str) -> str:
        if " " in v:
            raise ValueError("Username cannot contain spaces")
        return v.lower()   # Normalise to lowercase

user = User(username="AliceSmith")
print(user.username)  # alicesmith

# Raises ValidationError
user = User(username="Alice Smith")
# ValidationError: Value error, Username cannot contain spaces
```

<Note>
  The validator receives `cls` (the class, because no instance exists during validation) and `v` (the incoming value). Return the value to accept it — optionally transformed — or raise `ValueError` to reject it.
</Note>

<Tip>
  Use `@field_validator` only when built-in constraints aren't expressive enough. For simple range or length rules, `Field()` constraints are cleaner and self-documenting.
</Tip>

## Built-in special types

Pydantic ships with validated types for common formats, so you don't have to write regex validators yourself:

<Tabs>
  <Tab title="EmailStr">
    ```python theme={null}
    from pydantic import BaseModel, EmailStr

    class User(BaseModel):
        email: EmailStr  # Validates email format

    user = User(email="alice@example.com")   # ✅
    user = User(email="not-an-email")        # ❌ ValidationError
    ```

    <Note>Install the optional dependency: `pip install "pydantic[email]"`</Note>
  </Tab>

  <Tab title="HttpUrl">
    ```python theme={null}
    from pydantic import BaseModel, HttpUrl

    class Link(BaseModel):
        url: HttpUrl  # Must be a valid HTTP or HTTPS URL

    link = Link(url="https://example.com")   # ✅
    link = Link(url="ftp://files.example")   # ❌ ValidationError
    ```
  </Tab>

  <Tab title="Constrained list">
    ```python theme={null}
    from pydantic import BaseModel, Field

    class Order(BaseModel):
        items: list[str] = Field(min_length=1)  # At least one item required

    order = Order(items=["Widget", "Gadget"])   # ✅
    order = Order(items=[])                      # ❌ ValidationError
    ```
  </Tab>
</Tabs>

## Instance vs class attributes

When you need a constant shared across all model instances rather than a per-instance field, use `ClassVar`:

```python theme={null}
from typing import ClassVar
from pydantic import BaseModel

class Student(BaseModel):
    school: ClassVar[str] = "Academy of Code"  # Class attribute — not a field
    name: str
    age: int

student = Student(name="Alice", age=22)
print(student.school)         # Academy of Code
print(student.model_dump())   # {'name': 'Alice', 'age': 22}  — school not included
```

| Declaration                   | Behaviour                                                   |
| ----------------------------- | ----------------------------------------------------------- |
| `name: str`                   | Instance field — validated, serialised, per-instance        |
| `age: int = 18`               | Instance field with a default value                         |
| `school: ClassVar[str] = "X"` | Class attribute — shared, not serialised, not in `__init__` |

## JSON Schema generation

Pydantic can generate a JSON Schema from your model. FastAPI uses this to power its automatic interactive documentation:

```python theme={null}
from pydantic import BaseModel, Field

class User(BaseModel):
    name: str = Field(min_length=1, description="User's full name")
    age: int  = Field(ge=0, description="User's age in years")

import json
print(json.dumps(User.model_json_schema(), indent=2))
```

Output:

```json theme={null}
{
  "title": "User",
  "type": "object",
  "properties": {
    "name": {
      "description": "User's full name",
      "minLength": 1,
      "title": "Name",
      "type": "string"
    },
    "age": {
      "description": "User's age in years",
      "minimum": 0,
      "title": "Age",
      "type": "integer"
    }
  },
  "required": ["name", "age"]
}
```

<Accordion title="Real-world example: payment form validation">
  ```python theme={null}
  from pydantic import BaseModel, Field

  class PaymentForm(BaseModel):
      card_number: str   = Field(min_length=16, max_length=16)
      expiry_month: int  = Field(ge=1, le=12)
      expiry_year: int   = Field(ge=2024)
      cvv: str           = Field(min_length=3, max_length=4)
      amount: float      = Field(gt=0, description="Amount in USD")
      currency: str      = Field(default="USD", min_length=3, max_length=3)

  payment = PaymentForm(
      card_number="1234567890123456",
      expiry_month=12,
      expiry_year=2025,
      cvv="123",
      amount=99.99,
  )
  print(payment.model_dump())
  ```
</Accordion>

## Learn more

* [Fields documentation](https://docs.pydantic.dev/latest/concepts/fields/)
* [Validators documentation](https://docs.pydantic.dev/latest/concepts/validators/)
* [JSON Schema documentation](https://docs.pydantic.dev/latest/concepts/json_schema/)

***

## What's next?

You can now validate individual fields. Next, learn how to compose models inside other models to handle real-world hierarchical data.

<Card title="Nested Models" icon="arrow-right" href="/pydantic/nested-models">
  Learn how to handle complex, nested data structures with Pydantic.
</Card>
