Skip to content

pydantic_model_creator does not support Forward references using ReverseRelation in different files #1841

Open
@eyllanesc-JE

Description

@eyllanesc-JE

Describe the bug

If I use Forward references in the same file as I use ReverseRelation this does not generate problems.

import json

from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Tortoise
from tortoise import Model
from tortoise import fields


class Foo(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    bar = fields.ReverseRelation["Bar"]


class Bar(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    foo: fields.ForeignKeyRelation[Foo] = fields.ForeignKeyField(
        "models.Foo", on_delete=fields.CASCADE
    )


Tortoise.init_models(models_paths=["__main__"], app_label="models")
BarIn = pydantic_model_creator(Bar, name="BarIn")
print(json.dumps(BarIn.model_json_schema(), indent=4))

Output:

{
    "$defs": {
        "Foo_tpibnp_leaf": {
            "additionalProperties": false,
            "properties": {
                "id": {
                    "maximum": 2147483647,
                    "minimum": -2147483648,
                    "title": "Id",
                    "type": "integer"
                },
                "name": {
                    "maxLength": 255,
                    "title": "Name",
                    "type": "string"
                }
            },
            "required": [
                "id",
                "name"
            ],
            "title": "Foo",
            "type": "object"
        }
    },
    "additionalProperties": false,
    "properties": {
        "id": {
            "maximum": 2147483647,
            "minimum": -2147483648,
            "title": "Id",
            "type": "integer"
        },
        "name": {
            "maxLength": 255,
            "title": "Name",
            "type": "string"
        },
        "foo": {
            "$ref": "#/$defs/Foo_tpibnp_leaf"
        }
    },
    "required": [
        "id",
        "name",
        "foo"
    ],
    "title": "BarIn",
    "type": "object"
}

But if I separate them into different files:

├── models
│      ├───__init__.py
│      ├───bar.py
│      └───foo.py
└── main.py

foo.py

from tortoise import Model
from tortoise import fields

from models.bar import Bar


class Foo(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    bar = fields.ReverseRelation[Bar]

bar.py

from __future__ import annotations

from typing import TYPE_CHECKING

from tortoise import Model
from tortoise import fields

if TYPE_CHECKING:
    from models.foo import Foo


class Bar(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    foo: fields.ForeignKeyRelation[Foo] = fields.ForeignKeyField(
        "models.Foo", on_delete=fields.CASCADE
    )

main.py

from __future__ import annotations
import json

from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Tortoise
from models import foo, bar
from models.bar import Bar

Tortoise.init_models(models_paths=[foo, bar], app_label="models")
BarIn = pydantic_model_creator(Bar, name="BarIn")
print(json.dumps(BarIn.model_json_schema(), indent=4))

Getting the following:

Traceback (most recent call last):
  File "c:\Users\HP\Documents\apps\test_tortoirse_orm\main.py", line 10, in <module>
    BarIn = pydantic_model_creator(Bar, name="BarIn")
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\Documents\apps\test_tortoirse_orm\.venv\Lib\site-packages\tortoise\contrib\pydantic\creator.py", line 625, in pydantic_model_creator
    pmc = PydanticModelCreator(
          ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\Documents\apps\test_tortoirse_orm\.venv\Lib\site-packages\tortoise\contrib\pydantic\creator.py", line 300, in __init__
    self._annotations = get_annotations(cls)
                        ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\Documents\apps\test_tortoirse_orm\.venv\Lib\site-packages\tortoise\contrib\pydantic\utils.py", line 15, in get_annotations
    return typing.get_type_hints(method or cls)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\AppData\Local\Programs\Python\Python312\Lib\typing.py", line 2273, in get_type_hints
    value = _eval_type(value, base_globals, base_locals, base.__type_params__)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\AppData\Local\Programs\Python\Python312\Lib\typing.py", line 415, in _eval_type
    return t._evaluate(globalns, localns, type_params, recursive_guard=recursive_guard)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HP\AppData\Local\Programs\Python\Python312\Lib\typing.py", line 947, in _evaluate
    eval(self.__forward_code__, globalns, localns),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
NameError: name 'Foo' is not defined

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions