Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FieldInfo type_ method is missing #7220

Closed
1 task done
deivydaslipskis opened this issue Aug 23, 2023 · 4 comments
Closed
1 task done

FieldInfo type_ method is missing #7220

deivydaslipskis opened this issue Aug 23, 2023 · 4 comments
Assignees
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable

Comments

@deivydaslipskis
Copy link

deivydaslipskis commented Aug 23, 2023

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

Hello, I am migrating to Pydantic v2 and while migrating I can see that getting model_fields has changed:

v1 - model.__dict__["__fields__"]
v2 - model.model_fields

What also changed is what these two return:

v1 - {'id': ModelField(name='id', type=str, required=True), 'hq': ModelField(name='hq', type=Optional[Building], required=False, default=None)}
v2 - {'id': FieldInfo(annotation=str, required=True), 'hq': FieldInfo(annotation=Union[Building, NoneType], required=True)}

Since type_ method has been deprecated how can I check if the annotation is Pydantic BaseModel underneath?

In my specific case Building class inherits BaseModel not directly:

class Building(Name):
    address: str
    officers: list[str]
    
 class Name(BaseModel):
    """
    BaseModel subclass
    """

What I have tried:

if isinstance(value.annotation, BaseModel):
    # do something
# returns False when trying hq, although should return True, returns False with Name also
if issubclass(value.annotation, BaseModel):
    # do something
# returns TypeError: issubclass() arg 1 must be a class, same with Name class

Is checking the type_ like in the Pydantic v1 was forgotten to implement in Pydantic v2?

Example Code

from pydantic import BaseModel
from typing import Optional, get_origin, List


class Name(BaseModel):
    """
    BaseModel subclass
    """


class Building(Name):
    address: str
    officers: list[str]


class Person(Name):
    firstname: str
    lastname: Optional[str]


class Company(Name):
    id: str
    hq: Optional[Building]
    ceo: Person
    employees: List[Person]


model = Company(
    id="123",
    hq=Building(address="test1", officers=["test2"]),
    ceo=Person(firstname="test3", lastname="test4"),
    employees=[
        Person(firstname="test3", lastname="test4"),
        Person(firstname="test5", lastname="test6"),
    ],
)

#model_fields print:
#{'id': FieldInfo(annotation=str, required=True), 'hq': FieldInfo(annotation=Union[Building, NoneType], required=True), 'ceo': FieldInfo(annotation=Person, required=True), 'employees': FieldInfo(annotation=List[Person], required=True)}


for key, value in model.model_fields.items():
    if isinstance(value.annotation, BaseModel):
        print(f"Key - {key} is instance of BaseModel")
        # should print on hq, ceo keys

    if isinstance(value.annotation, Name):
        print(f"Key - {key} is instance of Name")
        # should print on hq, ceo keys

    if get_origin(value.annotation) == list:
        print(f"Key - {key} is list")
        # should print on employees key

    # if issubclass(value.annotation, BaseModel):
    #     TypeError: issubclass() arg 1 must be a class

    # if issubclass(value.annotation, Name):
    #     TypeError: issubclass() arg 1 must be a class


#How we were using this in Pydantic v1:

from pydantic.main import ModelMetaclass

for key, value in model.__dict__["__fields__"].items():
            if isinstance(value.type_, ModelMetaclass):
                # do something with hq, ceo
            elif get_origin(value.outer_type_) == list:
                # do something with employees

Python, Pydantic & OS Version

python 3.10
pydantic 2.1.0
ubuntu 22.04

Selected Assignee: @lig

@deivydaslipskis deivydaslipskis added bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable labels Aug 23, 2023
@samuelcolvin
Copy link
Member

A complete code example would help.

@deivydaslipskis
Copy link
Author

deivydaslipskis commented Aug 23, 2023

A complete code example would help.

I have updated code example, also added example of Pydantic v1 at the end

@adriangb
Copy link
Member

Is checking the type_ like in the Pydantic v1 was forgotten to implement in Pydantic v2?

It wasn't "forgotten", it's just not there because things are implemented differently. That was never meant to be a "feature" in v1 (and in fact there was a lot of weirdness around ModelField.type_ unpacking Optional[...] and such). You'll have to rework your code to use FieldInfo.annotation directly:

from pydantic import BaseModel

class Model(BaseModel):
    a: int
    b: float
    c: str


for field_name, value in Model.model_fields.items():
    print(field_name, value.annotation)

@mcgfeller
Copy link

typing.get_args(annotation) may come handy to get the type contained in a generic.

The V1 field.type_ seems to be equivalent to t[0] if (t := typing.get_args(field_info.annotation) else field_info.annotation) in many cases in V2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable
Projects
None yet
Development

No branches or pull requests

5 participants