How to define a Pydantic model nested under a class

Lav Sharma

I have two Pydantic models:

from typing import List, Union
from pydantic import BaseModel

class Students:
    class Student(BaseModel):
        StudentName: str
        StudentAge: int

    class StudentRequest(BaseModel):
        Class: int
        UUID: str
        Students: Union[List[Student], None]

For the above class at Students: Union[List[Student], None], I get the error Unresolved reference 'Student'. Can we not define a model under a class and use it for segregating them?

The code below works, but I want to get an understanding whether the above BaseModel nested under a class will work or not:

class Student(BaseModel):
    StudentName: str
    StudentAge: int

class StudentRequest(BaseModel):
    Class: int
    UUID: str
    Students: Union[List[Student], None]
Daniil Fajnberg

You need to understand that as long as the outer class is not fully constructed (when you are still setting up things inside its namespace), you will inevitably have to deal with forward references.

So there are two mandatory things (and one optional) you need to remember, when doing this.

1) Use the qualified class name OuterClass.InnerClass

The Python interpreter itself will have no trouble with a forward reference to another inner class in an annotation. That is simply because it does not actually do anything with those annotations by default. So you could just do this:

from pydantic import BaseModel


class OuterClass:
    class Student(BaseModel):
        name: str
        age: int

    class StudentRequest(BaseModel):
        ...
        students: list["Student"]

But this will fall apart with Pydantic models because those actually use those annotations to construct objects based off of them. As you will see in the next section, at some point Pydantic will have to actually resolve the refernce to Student so get the actual underlying class at runtime. And since that will inevitably happen outside the scope of the OuterClass, without the qualified name, it will run into a NameError.

So you have to do it like this:

...

class OuterClass:
    class Student(BaseModel):
        ...

    class StudentRequest(BaseModel):
        ...
        students: list["OuterClass.Student"]

2) Update forward references after the outer class is constructed

An annotation as shown above is internally stored as a ForwardRef object.

As mentioned above, Pydantic will have to resolve those forward references eventually, for you to be able to actually use those models.

However it is not always able to do so automatically. To quote the documentation:

In some cases, a ForwardRef won't be able to be resolved during model creation. [...] When this happens, you'll need to call update_forward_refs after the model has been created before it can be used.

But with a setup like yours, when the model is nested in the namespace of an outer class, you cannot just do so after the model is created. You must do that after the outer class is created.

So with that setup, you will have to do this:

from pydantic import BaseModel


class OuterClass:
    class Student(BaseModel):
        name: str
        age: int

    class StudentRequest(BaseModel):
        ...
        students: list["OuterClass.Student"]


OuterClass.StudentRequest.update_forward_refs()

Notice that the call happens outside of OuterClass after it is created.

3) Enable postponed evaluation of annotations (optional)

Since PEP 563 you can do from __future__ import annotations at the top of your module and then omit the quotes from your forward references. This just improves readability and makes things generally easier to code.

So in total, your code should look like this:

from __future__ import annotations
from pydantic import BaseModel


class OuterClass:
    class Student(BaseModel):
        name: str
        age: int

    class StudentRequest(BaseModel):
        ...
        students: list[OuterClass.Student]


OuterClass.StudentRequest.update_forward_refs()

Demo:

print(OuterClass.StudentRequest.schema_json(indent=4))
obj = OuterClass.StudentRequest.parse_obj({
    "students": [
        {"name": "foo", "age": 18},
        {"name": "bar", "age": 19},
    ]
})
print(obj.json(indent=4))

Output:

{
    "title": "StudentRequest",
    "type": "object",
    "properties": {
        "students": {
            "title": "Students",
            "type": "array",
            "items": {
                "$ref": "#/definitions/Student"
            }
        }
    },
    "required": [
        "students"
    ],
    "definitions": {
        "Student": {
            "title": "Student",
            "type": "object",
            "properties": {
                "name": {
                    "title": "Name",
                    "type": "string"
                },
                "age": {
                    "title": "Age",
                    "type": "integer"
                }
            },
            "required": [
                "name",
                "age"
            ]
        }
    }
}
{
    "students": [
        {
            "name": "foo",
            "age": 18
        },
        {
            "name": "bar",
            "age": 19
        }
    ]
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to define Pydantic Class for nested dictionary

how to model nested object with pydantic

pydantic nested model response

How to define query parameters using Pydantic model in FastAPI?

How do i use nested model with none value in pydantic

How to define nested class constants?

How to define nested model relationship with ValueObjects

How to define nested object model in Angular?

Individually specify nested dict fields in pydantic model

How to parse ObjectId in a pydantic model?

How to model an empty dictionary in pydantic

How to reshape the data using nested models with Pydantic

How to get values from nested pydantic classes?

How to validate a complex nested data structure with Pydantic?

How to define a nested object within model? (angular2/typescript)

How to use a reserved keyword in pydantic model

How do add an assembled field to a Pydantic model

How does `mypy` know the signature of the pydantic model?

return deeply nested json objects with response_model fastAPI and Pydantic

How to print sub-class name in pydantic?

Model class for nested json

How to define a nested template

How to define nested dictionary

In C++, how can I use a nested class type in an other nested class (the two nested classes are under the same outer class)

How model class "Name" becomes plural "Names" under Admin Page?

With Pydantic V2 and model_validate, how can I create a "computed field" from an attribute of an ORM model that IS NOT part of the Pydantic model

How to model java class after nested Json response

How to create a Room Entity using nested class model?

How the model class will be design to fetch the nested array object in recycler view?