PythonForEver
PythonForEver

Reputation: 476

Is it possible to access keyword arguments passed to a Field in a Pydantic BaseModel?

I need to access my_key in a Pydantic Field, as shown below:


class MyModel(BaseModel):
    x: str = Field(default=None, my_key=7)

    def print_field_objects(self):
        for obj in self.something_something:  # What do I use here
            print(obj.my_key)                 # ... so that i can use my_key?

I tried to see what self contains, like self.__dict__ but I wasn't able to find it. Is it even possible to access my_key?

I need it for my FastAPI endpoint.

Upvotes: 0

Views: 1331

Answers (2)

Gino Mempin
Gino Mempin

Reputation: 29546

You can generate the model's JSON Schema representation using the BaseModel's .schema() method, and then rely on this functionality of Field customization that:

** any other keyword arguments (e.g. examples) will be added verbatim to the field's schema

In other words, any other arbitrary keyword arguments passed to Field that isn't consumed or used by Pydantic (or by any custom creation/instantiation) would be present in that field's JSON Schema representation.

So, in this case, your my_key should be present on the model's schema:

In [4]: class MyModel(BaseModel):
   ...:     x: str = Field(default=None, my_key=7)
   ...: 

In [5]: MyModel.schema()
Out[5]: 
{'title': 'MyModel',
 'type': 'object',
 'properties': {'x': {'title': 'X', 'my_key': 7, 'type': 'string'}}}
                                    ^^^^^^^^^^^^
                                    ||||||||||||

You can then have an instance method that looks like this:

In [21]: class MyModel(BaseModel):
    ...:     x: str = Field(default=None, my_key=7)
    ...:     y: int = Field(default=1, my_key=42)
    ...: 
    ...:     def print_field_objects(self):
    ...:         for field_name, field in self.schema()["properties"].items():
    ...:             print(field["my_key"])
    ...: 

In [22]: m1 = MyModel()

In [23]: m1.print_field_objects()
7
42

But, since a model's schema and Field definitions are tied to the class, not the instance, multiple instances would have the same value:

In [28]: m1 = MyModel()

In [29]: m1.print_field_objects()
7
42

In [30]: m2 = MyModel()

In [31]: m2.print_field_objects()
7
42

So, it would be more accurate to make it a class method instead, since my_key won't change anyway with different values of the field x or with different instances:

In [35]: class MyModel(BaseModel):
    ...:     x: str = Field(default=None, my_key=7)
    ...:     y: int = Field(default=1, my_key=42)
    ...: 
    ...:     @classmethod
    ...:     def print_field_objects(cls):
    ...:         for field_name, field in cls.schema()["properties"].items():
    ...:             print(field_name, field.get("my_key"))
    ...: 

In [36]: MyModel.print_field_objects()
x 7
y 42

Upvotes: 2

Hedde van der Heide
Hedde van der Heide

Reputation: 22449

Field doesn't take arbitrary arguments, what exactly are you trying to achieve, perhaps there's a more appropriate solution.

Per your other question, x is a class attribute, whose definition can be found in self.__class__.__fields__, while its instance value can be found by calling self.x

Upvotes: 1

Related Questions