Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pydantic and "constructors"

I'm new to Pydantic and trying to understand how/if I can create a new class instance. I've read through the Pydantic documentation and can't find an example doing anything similar.

My python code (prior to Pydantic) looks like:

class Person:
    def __init__(self, id):
        db_result = get_db_content(id)    #lookup id in a database and return a result
        self.name = db_result['name']      
        self.birth_year = db_result['birth_year']

p1 = Person(1234)
print(p1.name)

What would the corresponding code in Pydantic look like if I want to create a Person instance based on an id? Also, is it possible with Pydantic to have multiple constructors for the same
class. For example:

p1 = Person(1234)
p2 = Person("Jane Doe")
like image 956
user2558918 Avatar asked Nov 20 '25 21:11

user2558918


2 Answers

I'm not sure if this is the most "pydantic" way to do things, but one approach to solving this problem is to use classmethods.

class Person(BaseModel):
    id: int
    name: str
    birth_year: int

    @classmethod
    def from_id(cls, id: int) -> "Person":
        db_result = get_db_content(id)    #lookup id in a database and return a result
        return cls(**db_result)

p1 = Person.from_id(1234)
print(p1.name)
like image 59
James P Avatar answered Nov 22 '25 12:11

James P


You could use a standard __init__ for this:

from typing import Optional

from pydantic import BaseModel

def get_db_content(id):
    return {
        'name': f'hello {id}',
        'birth_year': 2010,
    }

class Person(BaseModel):
    id: str
    name: Optional[str]
    birth_year: Optional[int]
    def __init__(self, *a, **kw):
        super().__init__(*a, **kw)
        db_result = get_db_content(self.id)
        self.name = db_result['name']      
        self.birth_year = db_result['birth_year']

person = Person(id='15')
print(person.dict())
# {'id': '15', 'name': 'hello 15', 'birth_year': 2010}

Although, personally I wouldn't do that. Pydantic classes are meant to be used as parsers/validators, not as fully functional object entities. Calling DB methods from a class like this directly couples your class to the db code and makes testing more difficult.

I would do this instead:

from pydantic import BaseModel

def get_db_content(id):
    return {
        'name': f'hello {id}',
        'birth_year': 2010,
    }

class Person(BaseModel):
    id: str
    name: str
    birth_year: int

person = Person(id='15', **get_db_content('15'))
print(person.dict())
# {'id': '15', 'name': 'hello 15', 'birth_year': 2010}

NB: the code examples should be self-contained and work as-is.

edit: using standard dunder constructor instead of @root_validator

like image 43
ierdna Avatar answered Nov 22 '25 11:11

ierdna



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!