drf-pydantic

repository·main·Indexed 40 days ago

https://github.com/georgebv/drf-pydantic

A library that allows developers to use Pydantic models as the source of truth for Django REST Framework (DRF) serializers. It provides automatic serializer generation via drf_pydantic.BaseModel, supports Pydantic validation during DRF's .is_valid() calls, and allows for custom field configuration using typing.Annotated or DrfPydanticSerializer.

Tokens
4.5K
Snippets
14
Records
21
Agent score
50%

What's inside drf-pydantic

  1. Enable Pydantic validation in DRF serializers

    main

    By default, drf-pydantic only uses DRF's validation. To run your complex Pydantic validators (e.g., @field_validator) during the DRF .is_valid() call, set "validate_pydantic": True in the drf_config dictionary of your model.

    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 trigger Pydantic validation
  2. Use drf_pydantic.BaseModel for automatic DRF serializers

    main

    Replace pydantic.BaseModel with drf_pydantic.BaseModel to automatically generate a DRF serializer via the .drf_serializer attribute. The generated serializer is equivalent to a standard DRF Serializer class based on your Pydantic field definitions.

    from drf_pydantic import BaseModel
    
    
    class MyModel(BaseModel):
        name: str
        addresses: list[str]
    
    # Access the generated DRF serializer class
    serializer_class = MyModel.drf_serializer
    
    # Use it like a normal DRF serializer
    my_value = MyModel.drf_serializer(data={"name": "Van", "addresses": ["Gym"]})
    my_value.is_valid(raise_exception=True)
  3. Configure DRF serializer fields manually

    main

    If the automatic generation is insufficient, you can configure fields in two ways:

    Per-Field Configuration

    Use typing.Annotated to attach DRF field classes to Pydantic fields.

    Custom Serializer

    Define a class inheriting from drf_pydantic.DrfPydanticSerializer and assign it to the model's drf_serializer attribute. Note: To ensure Pydantic validation works, your custom serializer must inherit from DrfPydanticSerializer.

    from typing import Annotated
    from drf_pydantic import BaseModel, DrfPydanticSerializer
    from rest_framework.serializers import CharField, IntegerField
    
    
    # Per-field configuration
    class Person(BaseModel):
        name: str
        age: Annotated[float, IntegerField(min_value=0, max_value=100)]
    
    
    # Custom Serializer
    class MyCustomSerializer(DrfPydanticSerializer):
        name = CharField(allow_null=False, required=True)
        age = IntegerField(allow_null=False, required=True)
    
    
    class PersonModel(BaseModel):
        name: str
        age: float
        drf_serializer = MyCustomSerializer
  4. Extend existing Pydantic models with DRF support

    main

    To add DRF serializer support to existing Pydantic models without rewriting them, use multiple inheritance. Important: drf_pydantic.BaseModel must come before pydantic.BaseModel in the inheritance list.

    from drf_pydantic import BaseModel as DRFBaseModel
    from pydantic import BaseModel
    
    
    class Pet(BaseModel):
        name: str
    
    
    class Dog(DRFBaseModel, Pet):
        breed: str
    
    # Now Dog has .drf_serializer
    serializer = Dog.drf_serializer
  5. Manually override DRF fields in Pydantic models

    main

    You can explicitly specify which DRF field should be used for a Pydantic field by including the DRF field instance in the field's metadata. This allows you to bypass the automatic mapping logic.

    Note: Only one DRF field instance can be provided per Pydantic field. Providing multiple will raise a FieldConversionError.

    from pydantic import BaseModel, Field
    from rest_framework import serializers
    from drf_pydantic.parse import create_serializer_from_model
    
    class CustomSchema(BaseModel):
        # Manually force the use of CharField with specific DRF arguments
        custom_field: str = Field(..., json_schema_extra=serializers.CharField(allow_blank=True))
    
    Serializer = create_serializer_from_model(CustomSchema)
  6. Configure Pydantic validation behavior via drf_config

    main

    The drf_config dictionary allows you to control how the generated DRF serializer interacts with the Pydantic model. All drf_config values are inherited by child classes.

    Available keys:

    • validate_pydantic (bool): If True, runs Pydantic validation during DRF .is_valid(). Defaults to False.
    • backpopulate_after_validation (bool): If True (default), updates the DRF serializer's validated_data with values modified by Pydantic validators. Set to False to prevent this.
    • validation_error (str): Determines the exception type raised. Set to "pydantic" to raise pydantic.ValidationError directly instead of wrapping it in DRF's ValidationError.
  7. Access the validated Pydantic instance from a serializer

    main

    If validate_pydantic is enabled and .is_valid() has been called successfully, you can access the actual Pydantic model instance (including any mutations from validators) via the .pydantic_instance property on the serializer.

    serializer = MyModel.drf_serializer(data={"name": "Van", "addresses": []})
    serializer.is_valid(raise_exception=True)
    # Access the Pydantic model directly
    print(serializer.pydantic_instance)  # MyModel(name='Van', addresses=[])
  8. Configure backpopulation of field values

    main

    When backpopulate_after_validation is set to True in _drf_config, the serializer will attempt to update the validated data dictionary (serializer.validated_data) with the values from the Pydantic model after validation.

    If a field in the Pydantic model is itself a pydantic.BaseModel, it will be automatically converted to a dictionary using .model_dump() before being placed back into the DRF validated data.

  9. Configure Pydantic validation error behavior

    main

    The DrfPydanticSerializer allows you to control how Pydantic ValidationError exceptions are handled via the _drf_config dictionary. You can choose between two modes for the validation_error key:

    • "pydantic": The original pydantic.ValidationError is raised directly.
    • "drf": Pydantic errors are caught and transformed into standard DRF serializers.ValidationError objects, mapping errors to specific fields or non_field_errors.
  10. Configure DRF settings via drf_config

    main

    You can customize how the Pydantic model integrates with Django REST Framework by defining a drf_config attribute of type DrfConfigDict on your model class.

    When multiple classes in an inheritance hierarchy define drf_config, the configuration is merged using the following priority (highest to lowest):

    1. The Pydantic model itself (cls)
    2. The parent class of the Pydantic model
    3. Default values

    Default values for drf_config are:

    • validate_pydantic=False
    • validation_error="drf"
    • backpopulate_after_validation=True
    from drf_pydantic.base_model import BaseModel
    from drf_pydantic.config import DrfConfigDict
    
    class MyModel(BaseModel):
        drf_config = DrfConfigDict(validation_error="custom_error")
        # ... fields ...
  11. Configure drf-pydantic serializer behavior via DrfConfigDict

    main

    When using drf-pydantic, you can pass a configuration dictionary (matching the DrfConfigDict schema) to control how the generated DRF serializer interacts with the underlying Pydantic model.

    Key configuration options include:

    • validate_pydantic (bool): Enables validation of the parent Pydantic model during the DRF serializer validation process. Defaults to False. This must be True for other configuration options to take effect.
    • validation_error (Literal["drf", "pydantic"]): Determines which error type is raised if Pydantic validation fails.
      • "drf": Raises a standard DRF ValidationError. This is the default and ensures compatibility with DRF views.
      • "pydantic": Raises a Pydantic ValidationError. Caution: This may make the serializer incompatible with DRF views that expect DRF-style error formats.
    • backpopulate_after_validation (bool): If True, the serializer will update its own field values using the data from the Pydantic model after validation is complete. This is essential if your Pydantic validators modify field values (e.g., normalization). Defaults to True.
    # Example configuration dictionary
    config = {
        "validate_pydantic": True,
        "validation_error": "drf",
        "backpopulate_after_validation": True
    }