Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I override fields from a Pydantic parent model to make them optional?

I have two pydantic classes like this.

class Parent(BaseModel):
    id: int
    name: str
    email: str

class ParentUpdate(BaseModel):
    id: Optional[int]
    name: Optional[str]
    email: Optional[str]

Both of these are practically the same but the Parent class makes all fields required. I want to use the Parent class for POST request body in FastAPI, hence all fields should be required. But I want to use the latter for PUT request body since the user can set selective fields and the remaining stays the same. I have taken a look at Required Optional Fields but they do not correspond to what I want to do.

If there was a way I could inherit the Parent class in ParentUpdate and modified all the fields in Parent to make them Optional that would reduce the clutter. Additionally, there are some validators present in the Parent class which I have to rewrite in the ParentUpdate class which I also want to avoid.

Is there any way of doing this? Thanks.

like image 710
Shiladitya Bose Avatar asked Jan 25 '21 11:01

Shiladitya Bose


People also ask

How do you make a field optional in pydantic?

Pydantic makes all the fields defined in the data model to be “required” by default. Alternatively, you can use Optional defined by the typing module in Python's standard library to make a field optional. This time, the output is a JSON. A JSON output is useful when working with APIs.

What is Orm_mode in pydantic?

ORM Mode (aka Arbitrary Class Instances) Pydantic models can be created from arbitrary class instances to support models that map to ORM objects. To do this: The Config property orm_mode must be set to True . The special constructor from_orm must be used to create the model instance.

What is alias in pydantic?

Alias Generator If data source field names do not match your code style (e. g. CamelCase fields), you can automatically generate aliases using alias_generator : Python 3.7 and above. from pydantic import BaseModel def to_camel(string: str) -> str: return ''. join(word.

What is pydantic BaseModel in Python?

pydantic allows custom data types to be defined or you can extend validation with methods on a model decorated with the validator decorator. dataclasses integration. As well as BaseModel , pydantic provides a dataclass decorator which creates (almost) vanilla Python dataclasses with input data parsing and validation.


1 Answers

You can make optional fields required in subclasses, but you cannot make required fields optional in subclasses. In fastapi author tiangolo's boilerplate projects, he utilizes a pattern like this for your example:

class ParentBase(BaseModel):
    """Shared properties."""
    name: str
    email: str

class ParentCreate(ParentBase):
    """Properties to receive on item creation."""
    # dont need id here if your db autocreates it
    pass

class ParentUpdate(ParentBase):
    """Properties to receive on item update."""
    # dont need id as you are likely PUTing to /parents/{id}
    # other fields should not be optional in a PUT
    # maybe what you are wanting is a PATCH schema?
    pass

class ParentInDBBase(ParentBase):
    """Properties shared by models stored in DB - !exposed in create/update."""
    # primary key exists in db, but not in base/create/update
    id: int                             

class Parent(ParentInDBBase):
    """Properties to return to client."""
    # optionally include things like relationships returned to consumer
    # related_things: List[Thing]
    pass

class ParentInDB(ParentInDBBase):
    """Additional properties stored in DB."""
    # could be secure things like passwords?
    pass

Yes, I agree this is incredibly verbose and I wish it wasn't. You still likely end up with other schemas more specific to particular forms in your UI. Obviously, you can remove some of these as they aren't necessary in this example, but depending on other fields in your DB, they may be needed, or you may need to set defaults, validation, etc.

In my experience for validators, you have to re-declare them but you can use a shared function, ie:

def clean_article_url(cls, v):
    return clean_context_url(v.strip())

class MyModel(BaseModel):
    article_url: str

    _clean_url = pydantic.validator("article_url", allow_reuse=True)(clean_article_url)
like image 79
shawnwall Avatar answered Sep 21 '22 14:09

shawnwall