Pydantic: Is it appropriate to implement a poly model using validate func?

Created on 2 Mar 2018  路  5Comments  路  Source: samuelcolvin/pydantic

sample code:

from enum import IntEnum

from pydantic import BaseModel


class FooTypeEnum(IntEnum):
    A = 1
    B = 2


class Foo(BaseModel):
    type: int

    @classmethod
    def validate(cls, value):
        type_to_model = {
            FooTypeEnum.A: FooA,
            FooTypeEnum.B: FooB,
            None: cls
        }
        _type = value.get('type', None)
        try:
            return type_to_model[_type](**value)
        except KeyError:
            raise TypeError(f'no model for type: {_type}')


class FooA(Foo):
    a: str


class FooB(Foo):
    b: str


class Bar(BaseModel):
    foo: Foo = ...


m = Bar(foo={'type': FooTypeEnum.A, 'a': 'aaa'})
print(m)
# Bar foo=<FooA type=1 a='aaa'>
print(m.dict())
# {'foo': {'type': 1, 'a': 'aaa'}}
m = Bar(foo={'type': FooTypeEnum.B, 'b': 'bbb'})
print(m)
# Bar foo=<FooB type=2 b='bbb'>
print(m.dict())
# {'foo': {'type': 2, 'b': 'bbb'}}

# m = Bar(foo={'type': FooTypeEnum.B, 'c': 'bbb'})
# error ...
# m = Bar(foo={'type': 3, 'c': 'bbb'})
# error ...
# m = Bar(foo={'c': 'bbb'})
# error ...
question

All 5 comments

I'm really not sure what your question is here. What does "poly model" mean?

In schematics, there is a PolyModelType.

"""A field that accepts an instance of any of the specified models."""

https://github.com/schematics/schematics/blob/master/schematics/types/compound.py#L333

And there is a example using PolyModelType to implement a Msg model (which could be TextMsg or ImageMsg): https://github.com/binderclip/code-snippets-python/blob/master/models/schematics_snippets/schematics_poly_model_type.py#L55-L81

I want to use pydantic to do the same thing.

Looks like you just need to use Union:

from typing import Union
from enum import IntEnum

from pydantic import BaseModel


class FooTypeEnum(IntEnum):
    A = 1
    B = 2


class Foo(BaseModel):
    type: FooTypeEnum


class FooA(Foo):
    a: str


class FooB(Foo):
    b: str


class Bar(BaseModel):
    foo: Union[FooA, FooB]


m = Bar(foo={'type': FooTypeEnum.A, 'a': 'aaa'})
print(m)
# Bar foo=<FooA type=1 a='aaa'>
print(m.dict())
# {'foo': {'type': 1, 'a': 'aaa'}}
m = Bar(foo={'type': FooTypeEnum.B, 'b': 'bbb'})
print(m)
# Bar foo=<FooB type=2 b='bbb'>
print(m.dict())
# {'foo': {'type': 2, 'b': 'bbb'}}

# m = Bar(foo={'type': FooTypeEnum.B, 'c': 'bbb'})
# error ...
# m = Bar(foo={'type': 3, 'c': 'bbb'})
# error ...
# m = Bar(foo={'c': 'bbb'})
# error ...

Pydantic tries each of the different types in Union to see if they're valid and returns the first which is.

Union can do the job even easier, thank you!

no problem, glad it helped.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bartekbrak picture bartekbrak  路  3Comments

iwoloschin picture iwoloschin  路  3Comments

mgresko picture mgresko  路  3Comments

ashpreetbedi picture ashpreetbedi  路  3Comments

engstrom picture engstrom  路  3Comments