Home > Software engineering >  Changing pydantic model Field() arguments with class variables for Fastapi
Changing pydantic model Field() arguments with class variables for Fastapi

Time:11-26

I'm a little new to tinkering with class inheritance in python, particularly when it comes down to using class attributes. In this case I am using a class attribute to change an argument in pydantic's Field() function. This wouldn't be too hard to do if my class contained it's own constructor, however, my class User1 is inheriting this from pydantic's BaseModel. The idea is that I would like to be able to change the class attribute prior to creating the instance.

Please see some example code below:

from pydantic import Basemodel, Field   

class User1(BaseModel):
    _set_ge = None # create class attribute
    item: float = Field(..., ge=_set_ge)

    # avoid overriding BaseModel's __init__
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
    
User1._set_ge = 0 # setting the class attribute to a new value
instance = User1(item=-1)
print(instance) # item=-1.0

When creating the instance using instance = User1(item=-1) I would expect a validation error to be thrown, but it instead passes validation and simply returns the item value.

If I had my own constructor there would be little issue in changing the _set_ge, but as User1 inheriting this constructor from BaseModel, things are a little more complicated.

The eventual aim is to add this class to a fastapi endpoint as follows:

from fastapi import Fastapi
from schemas import User1    

class NewUser1(User1):
      pass

NewUser1._set_ge = 0    

@app.post("/")
def endpoint(request: NewUser1):
    return User1.item

To reduce code duplication, I aimed to use this method to easily change Field() arguments. If there is a better way, I'd be glad to consider that too.

This question is quite closely related to this unanswered one.

CodePudding user response:

In the end, the @validator proposal by @hernán-alarcón is probably the best way to do this. For example:

from pydantic import Basemodel, Field
from typing import ClassVar   

class User(BaseModel):
    _set_ge = ClassVar[float] # added the ClassVar typing to make clearer, but the underscore should be sufficient
    item: float = Field(...)

    @validator('item')
    def limits(cls, v):
        limit_number = cls._set_ge
        if v >= limit_number:
            return v
        else:
            raise NumberNotGeError(limit_value=limit_number)
        
    
class User1(User)
     _set_ge = 0 # setting the class attribute to a new value

instance = User1(item=-1) # raises the error
  • Related