pydantic icon indicating copy to clipboard operation
pydantic copied to clipboard

Support `extra` specification on a field-by-field basis

Open ArtemIsmagilov opened this issue 1 year ago • 4 comments

Initial Checks

  • [X] I have searched Google & GitHub for similar requests and couldn't find anything
  • [X] I have read and followed the docs and still think this feature is missing

Description

I know that we can just ignore the additional fields for the schema. However, I want to specify certain fields that need to be ignored, for example at different resolution levels.

Example.

from pydantic import BaseModel, ConfigDict


class User(BaseModel):
    model_config = ConfigDict(skip={'age': 'ignore', 'other': 'ignore'})  

    name: str


user = User(name='John Doe', age=20, job='writer')  
print(user)
#> name='John Doe'
from pydantic import BaseModel, ConfigDict


class User(BaseModel):
    model_config = ConfigDict(skip={'age': 'allow', 'other': 'ignore'})  

    name: str


user = User(name='John Doe', age=20, job='writer')  
print(user)
#> name='John Doe' age=20

...

Affected Components

ArtemIsmagilov avatar Jun 20 '24 15:06 ArtemIsmagilov

Hi @ArtemIsmagilov,

I could also see the API getting tricky, for example, what if someone wants to allow an extra field called other? Perhaps we could implement something like

ConfigDict(extra=Extra(forbid={...}, ignore={...}, allow={...}))

where you could set wildcard * values to specify the extra behavior for all remaining unspecified fields.

In the meantime, you can use a custom validator:

from typing import Any

from pydantic import BaseModel, ConfigDict, model_validator


class User(BaseModel):
    model_config = ConfigDict(extra='allow')  

    name: str

    @model_validator(mode='before')
    @classmethod
    def check_extras(cls, data: Any) -> Any:
        if isinstance(data, dict):
            allowed_keys = [*cls.model_fields.keys(), 'age']
            return {k: v for k, v in data.items() if k in allowed_keys}
        return data


user = User(name='John Doe', age=20, job='writer')  
print(repr(user))
#> User(name='John Doe', age=20)

sydney-runkle avatar Jun 20 '24 17:06 sydney-runkle

Great, thanks

ArtemIsmagilov avatar Jun 20 '24 17:06 ArtemIsmagilov

@sydney-runkle Are there any plans to implement this functionality?

ArtemIsmagilov avatar Jun 22 '24 15:06 ArtemIsmagilov

@ArtemIsmagilov,

I haven't yet added it to a milestone. It'll require pretty significant changes in pydantic-core.

I'd be happy to support development on this front with PR reviews, etc if anyone is interested in taking this on, but I don't think we'll pick this up until we've addressed some other more pressing things like performance improvements, etc.

sydney-runkle avatar Jun 24 '24 12:06 sydney-runkle