### Install drf-pydantic Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Install the library using pip. This command installs the latest version compatible with pydantic v2. ```shell pip install drf-pydantic ``` -------------------------------- ### Example Request and Responses Source: https://context7.com/georgebv/drf-pydantic/llms.txt Illustrates a sample curl request to the API endpoint and the expected success and validation failure responses. ```bash # Example request (curl): # curl -X POST http://localhost:8000/users/ \ # -H "Content-Type: application/json" \ # -d '{"username": "alice", "email": "alice@example.com", "age": 25}' # # Success response (201): # {"username": "alice", "email": "alice@example.com", "age": 25, "bio": null} # # Validation failure response (400): # {"age": ["Ensure this value is greater than or equal to 18."]} ``` -------------------------------- ### Map Pydantic Constraints to DRF Fields Source: https://context7.com/georgebv/drf-pydantic/llms.txt Pydantic field constraints and metadata are automatically translated to DRF field arguments. This example demonstrates various mappings including string constraints, patterns, numeric ranges, enums, literals, optional fields, and JSON values. ```python import typing import enum import pydantic from drf_pydantic import BaseModel class Priority(enum.Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" class Task(BaseModel): # description → help_text, title → label title: str = pydantic.Field(title="Task Title", description="Short summary") # StringConstraints → min_length, max_length, allow_blank slug: typing.Annotated[str, pydantic.StringConstraints(min_length=3, max_length=50)] # pattern → RegexField code: typing.Annotated[str, pydantic.Field(pattern=r'^TASK-\d+$')] # ge/gt → min_value, le/lt → max_value (numeric only) points: typing.Annotated[int, pydantic.Field(ge=0, le=100)] # enum → ChoiceField priority: Priority # Literal → ChoiceField status: typing.Literal["open", "closed", "in_progress"] = "open" # Optional → allow_null=True assignee: typing.Optional[str] = None # pydantic.JsonValue → JSONField metadata: pydantic.JsonValue = None s = Task.drf_serializer() from rest_framework import serializers assert s.fields["title"].label == "Task Title" assert s.fields["title"].help_text == "Short summary" assert s.fields["slug"].min_length == 3 assert s.fields["slug"].max_length == 50 assert isinstance(s.fields["code"], serializers.RegexField) assert s.fields["points"].min_value == 0 assert s.fields["points"].max_value == 100 assert isinstance(s.fields["priority"], serializers.ChoiceField) assert isinstance(s.fields["status"], serializers.ChoiceField) assert s.fields["assignee"].allow_null is True assert isinstance(s.fields["metadata"], serializers.JSONField) ``` -------------------------------- ### Extend Existing Pydantic Models with drf_pydantic Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Illustrates how to add drf_serializer functionality to existing Pydantic models by inheriting from drf_pydantic.BaseModel. Ensure drf_pydantic.BaseModel precedes pydantic.BaseModel in the inheritance order. ```python from drf_pydantic import BaseModel as DRFBaseModel from pydantic import BaseModel class Pet(BaseModel): name: str class Dog(DRFBaseModel, Pet): breed: str Dog.drf_serializer ``` -------------------------------- ### Access Validated Pydantic Instance Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Shows how to access the validated Pydantic model instance via the 'pydantic_instance' property after calling .is_valid(). This is available when 'validate_pydantic' is enabled. Accessing before .is_valid() or when disabled will raise an error. ```python serializer = MyModel.drf_serializer(data={"name": "Van", "addresses": []}) serializer.is_valid(raise_exception=True) print(serializer.pydantic_instance) # MyModel(name='Van', addresses=[]) ``` -------------------------------- ### Create Serializer Programmatically with create_serializer_from_model Source: https://context7.com/georgebv/drf-pydantic/llms.txt The create_serializer_from_model function generates a DrfPydanticSerializer subclass from a Pydantic model. It caches results to ensure the same Pydantic model always returns the same serializer class, and can be used for dynamic use cases. ```python import pydantic from drf_pydantic.parse import create_serializer_from_model from drf_pydantic.config import DrfConfigDict class Location(pydantic.BaseModel): lat: float lon: float label: str = "unknown" config: DrfConfigDict = { "validate_pydantic": False, "validation_error": "drf", "backpopulate_after_validation": True, } LocationSerializer = create_serializer_from_model(Location, drf_config=config) print(LocationSerializer.__name__) # LocationSerializer s = LocationSerializer(data={"lat": 40.7128, "lon": -74.0060}) s.is_valid(raise_exception=True) print(s.validated_data) # {'lat': 40.7128, 'lon': -74.006, 'label': 'unknown'} # Second call returns the cached class — same object identity LocationSerializer2 = create_serializer_from_model(Location, drf_config=config) assert LocationSerializer is LocationSerializer2 ``` -------------------------------- ### Model Inheritance with drf-pydantic Source: https://context7.com/georgebv/drf-pydantic/llms.txt drf-pydantic.BaseModel can be inserted into an existing inheritance chain. The `drf_serializer` includes fields from all ancestors. `drf_pydantic.BaseModel` must precede `pydantic.BaseModel` in the inheritance order. ```python from pydantic import BaseModel as PydanticBaseModel from drf_pydantic import BaseModel as DRFBaseModel # Existing codebase models — untouched class Animal(PydanticBaseModel): name: str species: str class Pet(Animal): owner: str # Inject DRF support only where needed class RegisteredPet(DRFBaseModel, Pet): registration_id: str serializer = RegisteredPet.drf_serializer(data={ "name": "Rex", "species": "Dog", "owner": "Alice", "registration_id": "PET-001", }) serializer.is_valid(raise_exception=True) print(list(serializer.fields.keys())) # ['name', 'species', 'owner', 'registration_id'] # Child classes also inherit drf_config from parents: class Employee(DRFBaseModel): name: str drf_config = {"validate_pydantic": True} class Manager(Employee): reports: int # inherits validate_pydantic=True automatically print(Manager.drf_config["validate_pydantic"]) ``` -------------------------------- ### Accessing Validated Pydantic Instance Source: https://context7.com/georgebv/drf-pydantic/llms.txt Access the fully constructed and validated Pydantic model instance via `.pydantic_instance` after calling `.is_valid()` with `validate_pydantic: True`. Accessing before `.is_valid()` raises an `AssertionError`. ```python import pydantic from drf_pydantic import BaseModel class Order(BaseModel): item: str quantity: int price: float @pydantic.model_validator(mode="after") def compute_total(self): # attach a computed attribute not declared as a field self.__dict__["total"] = self.quantity * self.price return self drf_config = {"validate_pydantic": True} serializer = Order.drf_serializer(data={"item": "Widget", "quantity": 3, "price": 9.99}) serializer.is_valid(raise_exception=True) instance: Order = serializer.pydantic_instance print(instance) # item='Widget' quantity=3 price=9.99 print(instance.__dict__["total"]) # 29.97 # Accessing before is_valid() raises AssertionError: bad = Order.drf_serializer(data={"item": "X", "quantity": 1, "price": 1.0}) try: _ = bad.pydantic_instance # raises AssertionError: call .is_valid() first except AssertionError as e: print(e) ``` -------------------------------- ### Define Pydantic Model for User Request Source: https://context7.com/georgebv/drf-pydantic/llms.txt Define a Pydantic model inheriting from drf_pydantic.BaseModel to represent API request data. Use drf_config to enable Pydantic validation within DRF. ```python import pydantic from typing import Optional from drf_pydantic import BaseModel class CreateUserRequest(BaseModel): username: str = pydantic.Field(min_length=3, max_length=30) email: pydantic.EmailStr age: int = pydantic.Field(ge=18) bio: Optional[str] = None drf_config = {"validate_pydantic": True} ``` -------------------------------- ### Define a Pydantic Model for DRF Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Use drf_pydantic.BaseModel instead of pydantic.BaseModel to create models that can be automatically converted to DRF serializers. The generated DRF serializer is accessible via the `drf_serializer` attribute. ```python from drf_pydantic import BaseModel class MyModel(BaseModel): name: str addresses: list[str] ``` -------------------------------- ### Generate Serializers for Nested Pydantic Models Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Demonstrates that drf-pydantic automatically generates serializers for all standard nested Pydantic models when a top-level model is updated to inherit from drf_pydantic.BaseModel. ```python from drf_pydantic import BaseModel as DRFBaseModel from pydantic import BaseModel class Apartment(BaseModel): floor: int tenant: str class Building(BaseModel): address: str apartments: list[Apartment] class Block(DRFBaseModel): buildings: list[Building] Block.drf_serializer ``` -------------------------------- ### Configure Serializer Validation Behavior with drf_config Source: https://context7.com/georgebv/drf-pydantic/llms.txt Use the `drf_config` attribute to control Pydantic validation within DRF. Settings include `validate_pydantic`, `validation_error`, and `backpopulate_after_validation`. These settings are inherited by child classes. ```python import pydantic from drf_pydantic import BaseModel class Person(BaseModel): name: str age: int @pydantic.field_validator("name") @classmethod def titlecase_name(cls, v: str) -> str: return v.strip().title() # mutates the value @pydantic.model_validator(mode="after") def check_adult(self): if self.age < 18: raise ValueError("Must be 18 or older") return self drf_config = { "validate_pydantic": True, # run pydantic validators inside DRF "validation_error": "drf", # wrap pydantic errors as DRF errors "backpopulate_after_validation": True, # write mutated values back to validated_data } serializer = Person.drf_serializer(data={"name": " john doe ", "age": 25}) serializer.is_valid(raise_exception=True) print(serializer.validated_data) # {'name': 'John Doe', 'age': 25} ← field_validator mutation reflected invalid = Person.drf_serializer(data={"name": "Bob", "age": 16}) invalid.is_valid() # returns False print(invalid.errors) ``` -------------------------------- ### Integrate Pydantic Model in DRF APIView Source: https://context7.com/georgebv/drf-pydantic/llms.txt Use the generated DRF serializer from the Pydantic model within a DRF APIView. Access the validated Pydantic instance via `serializer.pydantic_instance`. ```python from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class UserCreateView(APIView): def post(self, request): serializer = CreateUserRequest.drf_serializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # Access the fully typed Pydantic instance user_data: CreateUserRequest = serializer.pydantic_instance # ... persist user_data to database ... return Response(serializer.validated_data, status=status.HTTP_201_CREATED) ``` -------------------------------- ### Enable Pydantic Validation in DRF Serializer Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Configure the model to use pydantic's validation alongside DRF's by setting `"validate_pydantic": True` in the `drf_config` dictionary. This ensures that pydantic validators run when `.is_valid()` is called on the generated DRF serializer. ```python from drf_pydantic import BaseModel class MyModel(BaseModel): name: str addresses: list[str] drf_config = {"validate_pydantic": True} my_serializer = MyModel.drf_serializer(data={"name": "Van", "addresses": []}) my_serializer.is_valid() # this will also validate MyModel ``` -------------------------------- ### Define Custom DRF Serializer for Pydantic Model Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Create a custom DRF serializer inheriting from DrfPydanticSerializer to manually define fields. This is useful when drf-pydantic's auto-generation does not meet specific requirements. Ensure the custom serializer is a subclass of DrfPydanticSerializer for consistent Pydantic validation. ```python from drf_pydantic import BaseModel, DrfPydanticSerializer from rest_framework.serializers import CharField, IntegerField class MyCustomSerializer(DrfPydanticSerializer): name = CharField(allow_null=False, required=True) age = IntegerField(allow_null=False, required=True) class Person(BaseModel): name: str age: float drf_serializer = MyCustomSerializer class Employee(Person): salary: float class Company(BaseModel): ceo: Person ``` -------------------------------- ### Configure DRF IntegerField for Pydantic Field Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Use Annotated to specify DRF serializer field properties like min_value and max_value for a pydantic field. ```python from typing import Annotated from drf_pydantic import BaseModel from rest_framework.serializers import IntegerField class Person(BaseModel): name: str age: Annotated[float, IntegerField(min_value=0, max_value=100)] ``` -------------------------------- ### Define Pydantic BaseModel for DRF Serializer Generation Source: https://context7.com/georgebv/drf-pydantic/llms.txt Subclass `drf_pydantic.BaseModel` to automatically generate a DRF serializer. The model behaves like a standard Pydantic model, with the serializer attached as `MyModel.drf_serializer`. ```python import datetime import decimal import uuid from typing import Optional import pydantic from drf_pydantic import BaseModel class Employee(BaseModel): id: uuid.UUID name: str email: pydantic.EmailStr salary: decimal.Decimal hired_on: datetime.date department: Optional[str] = None # allow_null=True, required=True is_active: bool = True # required=False, default=True # At class creation time, Employee.drf_serializer is now equivalent to: # class EmployeeSerializer(DrfPydanticSerializer): # id = UUIDField(allow_null=False, required=True) # name = CharField(allow_null=False, required=True, allow_blank=True) # email = EmailField(allow_null=False, required=True, allow_blank=False) # salary = DecimalField(allow_null=False, required=True, ...) # hired_on = DateField(allow_null=False, required=True) # department= CharField(allow_null=True, required=True) # is_active = BooleanField(allow_null=False, required=False, default=True) serializer = Employee.drf_serializer(data={ "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Alice", "email": "alice@example.com", "salary": "95000.00", "hired_on": "2023-06-15", "department": None, }) if serializer.is_valid(): print(serializer.validated_data) # OrderedDict([('id', UUID('550e8400...')), ('name', 'Alice'), ...]) else: print(serializer.errors) ``` -------------------------------- ### Assign Custom Serializer Class Source: https://context7.com/georgebv/drf-pydantic/llms.txt Assign a custom serializer class, subclassing `DrfPydanticSerializer`, directly to the `drf_serializer` attribute of a `BaseModel`. This custom serializer will be used for that model and any nested instances. Child classes do not inherit manually set `drf_serializer`. ```python from drf_pydantic import BaseModel, DrfPydanticSerializer from rest_framework.serializers import CharField, IntegerField, ChoiceField class PersonSerializer(DrfPydanticSerializer): name = CharField(allow_null=False, required=True, max_length=100) age = IntegerField(allow_null=False, required=True, min_value=0) role = ChoiceField(choices=["admin", "user", "guest"], required=False, default="user") class Person(BaseModel): name: str age: int role: str = "user" drf_serializer = PersonSerializer # use custom serializer class Company(BaseModel): ceo: Person # nested → uses PersonSerializer automatically headcount: int class Employee(Person): salary: float # Employee gets its OWN auto-generated serializer s = Company.drf_serializer(data={ "ceo": {"name": "Alice", "age": 45, "role": "admin"}, "headcount": 200, }) s.is_valid(raise_exception=True) print(s.fields["ceo"].__class__.__name__) # PersonSerializer print(s.validated_data["ceo"]["role"]) # admin emp_s = Employee.drf_serializer(data={"name": "Bob", "age": 30, "salary": 75000.0}) emp_s.is_valid(raise_exception=True) print(emp_s.__class__.__name__) # EmployeeSerializer (auto-generated) ``` -------------------------------- ### Update DRF Serializer Fields from Pydantic Model Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Demonstrates how drf-pydantic updates DRF serializer values from a validated Pydantic model. This behavior can be disabled by setting 'backpopulate_after_validation' to False in drf_config. ```python from drf_pydantic import BaseModel class MyModel(BaseModel): name: str addresses: list[str] @pydantic.field_validator("name") @classmethod def validate_name(cls, v): assert isinstance(v, str) return v.strip().title() drf_config = {"validate_pydantic": True} my_serializer = MyModel.drf_serializer(data={"name": "van herrington", "addresses": []}) my_serializer.is_valid() print(my_serializer.validated_data) # {"name": "Van Herrington", "addresses": []} ``` ```python class MyModel(BaseModel): ... # other fields and methods drf_config = {"validate_pydantic": True, "backpopulate_after_validation": False} ``` -------------------------------- ### Raise Pydantic ValidationError Directly Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Configures drf-pydantic to raise Pydantic's ValidationError directly instead of wrapping it in DRF's ValidationError. Note that DRF's ValidationError is still raised first if DRF validation fails, and this setting may break views expecting DRF's ValidationError. ```python import pydantic from drf_pydantic import BaseModel class MyModel(BaseModel): name: str addresses: list[str] @pydantic.field_validator("name") @classmethod def validate_name(cls, v): assert isinstance(v, str) if v != "Billy": raise ValueError("Wrong door") return v drf_config = {"validate_pydantic": True, "validation_error": "pydantic"} my_serializer = MyModel.drf_serializer(data={"name": "Van", "addresses": []}) my_serializer.is_valid() # this will raise pydantic.ValidationError ``` -------------------------------- ### Access DRF Serializer from Pydantic Model Source: https://github.com/georgebv/drf-pydantic/blob/main/README.md Retrieve the DRF serializer class generated from a drf_pydantic.BaseModel. This serializer can then be instantiated and used like any other DRF serializer for data validation. ```python my_value = MyModel.drf_serializer(data={"name": "Van", "addresses": ["Gym"]}) my_value.is_valid(raise_exception=True) ``` -------------------------------- ### Use DrfPydanticSerializer Base Class Source: https://context7.com/georgebv/drf-pydantic/llms.txt DrfPydanticSerializer is a generic subclass of DRF's Serializer that adds pydantic_instance property support and the validate() hook for Pydantic validation. It should be inherited by all auto-generated and custom serializers. ```python from drf_pydantic import BaseModel, DrfPydanticSerializer from rest_framework.serializers import CharField import pydantic class AddressSerializer(DrfPydanticSerializer): street = CharField(required=True) city = CharField(required=True) zip = CharField(required=True, max_length=10) class Address(BaseModel): street: str city: str zip: str @pydantic.model_validator(mode="after") def validate_zip_format(self): if not self.zip.isdigit(): raise ValueError("ZIP must be numeric") return self drf_serializer = AddressSerializer drf_config = {"validate_pydantic": True} # Confirm the serializer is linked back to the model print(AddressSerializer._pydantic_model) # print(Address.drf_config["validate_pydantic"]) # True good = Address.drf_serializer(data={"street": "1 Main", "city": "NYC", "zip": "10001"}) good.is_valid(raise_exception=True) print(good.pydantic_instance) # street='1 Main' city='NYC' zip='10001' bad = Address.drf_serializer(data={"street": "1 Main", "city": "NYC", "zip": "ABCDE"}) bad.is_valid() print(bad.errors) # {'non_field_errors': ['{"loc": [], "msg": "Value error, ZIP must be numeric", ...} kualitas']} ``` -------------------------------- ### Override DRF Fields with Annotated Source: https://context7.com/georgebv/drf-pydantic/llms.txt Use `typing.Annotated` to inject custom DRF `Field` instances for specific model attributes. This bypasses automatic introspection for that field, using the provided DRF field as-is. Ensure the DRF field's type matches the annotated type. ```python import typing import decimal from drf_pydantic import BaseModel from rest_framework.serializers import IntegerField, DecimalField, RegexField class Product(BaseModel): name: str # Force age-like int to have min/max bounds rating: typing.Annotated[int, IntegerField(min_value=1, max_value=5, required=True)] # Precise decimal control price: typing.Annotated[ decimal.Decimal, DecimalField(max_digits=10, decimal_places=2, required=True, allow_null=False), ] # Regex-validated SKU sku: typing.Annotated[str, RegexField(regex=r'^[A-Z]{2}-\d{4}$', required=True)] serializer = Product.drf_serializer(data={ "name": "Widget", "rating": 4, "price": "19.99", "sku": "WD-1234", }) serializer.is_valid(raise_exception=True) print(serializer.validated_data) # {'name': 'Widget', 'rating': 4, 'price': Decimal('19.99'), 'sku': 'WD-1234'} bad = Product.drf_serializer(data={"name": "X", "rating": 6, "price": "5.0", "sku": "bad"}) bad.is_valid() print(bad.errors) # {'rating': ['Ensure this value is less than or equal to 5.'], # 'sku': ['This value does not match the required pattern.']} ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.