Search code examples
pythonvalidationpydantic

Pydantic 2.* validator to add all extra fields to unique dict field


Both validators and extra columns have different ways of configuration in pydantic 2.*, I would like to know if I can still do a validation to take all the extra fields and put them in a single dictionary field

The expected behavior is:

class A(BaseModel):
    name: str
    extra_columns: Optional[Dict]

a = A(name="john", age=24, address="Brazil")

print(a)

>>> A(name="john", extra_columns={"age": 24, "address": "Brazil"})

My previous code:

    @root_validator(pre=True)
    def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        all_required_field_names = {
            field.alias for field in cls.__fields__.values() if field.alias != "extra_columns"
        }

        extra: Dict[str, Any] = {}
        for field_name in list(values):
            if field_name not in all_required_field_names:
                extra[field_name] = values.pop(field_name)
        values["extra_columns"] = extra
        return values

Is possible to take the same behavior in pydantic 2.*?


Solution

  • This can be solved with model_validator:

    from typing import Any
    from pydantic import BaseModel, model_validator
    
    class A(BaseModel):
        name: str
        extra_columns: dict | None = None
        
        @model_validator(mode="before")
        @classmethod
        def set_extra_columns(cls, data: Any):
            if isinstance(data, dict):
                extra_fields = data.keys() - cls.model_fields.keys()
                if extra_fields:
                    if "extra_columns" not in data:
                        data["extra_columns"] = {}
                    data["extra_columns"].update(
                        {field_name: data[field_name] for field_name in extra_fields}
                    )
            return data
    
    a = A(name="john", age=24, address="Brazil")
    
    print(a)
    
    # >>> name='john' extra_columns={'age': 24, 'address': 'Brazil'}