Skip to content

Instantly share code, notes, and snippets.

@devxoul
Last active September 3, 2024 23:14
Show Gist options
  • Save devxoul/229ec70708e89e12905830669a847238 to your computer and use it in GitHub Desktop.
Save devxoul/229ec70708e89e12905830669a847238 to your computer and use it in GitHub Desktop.
Pydantic Partial Model
from copy import copy
from typing import Generic, Optional, TypeVar, get_args, get_type_hints
from pydantic import BaseModel
from pydantic.errors import ConfigError
from pydantic.typing import convert_generics
T = TypeVar('T', bound=BaseModel)
class Partial(Generic[T], BaseModel):
class Config:
abstract = True
def __init_subclass__(cls, *args, **kwargs):
orig_bases = getattr(cls, '__orig_bases__')
base_class = get_args(orig_bases[0])[0]
base_fields = base_class.__fields__
type_hints = get_type_hints(base_class)
config = getattr(cls, 'Config', None)
includes = getattr(config, 'include', [])
excludes = getattr(config, 'exclude', [])
if includes and excludes:
raise ConfigError('include and exclude are mutually exclusive')
field_names = set(base_fields.keys())
if includes and set(includes) - field_names:
raise ConfigError(f'{includes[0]} is not a valid field name')
if excludes and set(excludes) - field_names:
raise ConfigError(f'{excludes[0]} is not a valid field name')
for name, field in base_fields.items():
if includes and name not in includes:
continue
if excludes and name in excludes:
continue
optional_field = copy(field)
if field.required:
optional_field.required = False
optional_field.allow_none = True
if getattr(optional_field, 'default_factory', None):
optional_field.default_factory = lambda: None
else:
optional_field.default = None
cls.__fields__[name] = optional_field
cls.__annotations__[name] = Optional[type_hints[name]]
if not cls.__doc__:
cls.__doc__ = base_class.__doc__ or ''
from pydantic import BaseModel
class Item(BaseModel):
a: str
b: int
c: str
d: int
class UpdateItemInput(Partial[Item]):
class Config:
include = ['a', 'b']
# or
exclude = ['c', 'd']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment