-
-
Notifications
You must be signed in to change notification settings - Fork 399
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
pydantic_model_creator does not support Forward references using ReverseRelation in different files #1841
Comments
Checking I found that the problem has its root in the following code: def get_annotations(cls: "Type[Model]", method: Optional[Callable] = None) -> Dict[str, Any]:
"""
Get all annotations including base classes
:param cls: The model class we need annotations from
:param method: If specified, we try to get the annotations for the callable
:return: The list of annotations
"""
return typing.get_type_hints(method or cls) And looking at the change history I implemented the following modification: def get_annotations(cls: "Type[Model]", method: Optional[Callable] = None) -> Dict[str, Any]:
"""
Get all annotations including base classes
:param cls: The model class we need annotations from
:param method: If specified, we try to get the annotations for the callable
:return: The list of annotations
"""
localns = (
tortoise.Tortoise.apps.get(cls._meta.app, None)
if cls._meta.app
else None
)
return typing.get_type_hints(method or cls, localns=localns) Solving my problem and getting: {
"$defs": {
"Foo_ijnnvp_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_ijnnvp_leaf"
}
},
"required": [
"id",
"name",
"foo"
],
"title": "BarIn",
"type": "object"
} My solution is different from what was generated by issue #1552, It indicates there that the error is due to setting the globalns. |
@eyllanesc-JE is it the complete traceback?
|
just change if TYPE_CHECKING:
from foo import Foo to from foo import Foo during runtime, TYPE_CHECKING is always False. So it won't import Foo. Full example, working with python3.12: first option with ForeignKeyRelationmain.py import json
from typing import Callable, Coroutine
from tortoise import run_async, Tortoise
from tortoise.contrib.pydantic import pydantic_model_creator
from bar import Bar
def run(func: Callable[..., Coroutine]) -> None:
run_async(func())
async def do_stuff():
await Tortoise.init(
db_url='sqlite:///:memory:',
modules={'models': ['foo', 'bar']}
)
BarIn = pydantic_model_creator(Bar, name="BarIn")
print(json.dumps(BarIn.model_json_schema(), indent=2))
if __name__ == '__main__':
run(do_stuff) foo.py from tortoise import fields
from tortoise import Model
class Foo(Model):
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=255) bar.py from tortoise import fields
from tortoise import Model
from 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
) second option without ForeignKeyRelationbar.py from tortoise import fields
from tortoise import Model
class Bar(Model):
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=255)
foo = fields.ForeignKeyField(
"models.Foo", on_delete=fields.CASCADE
) |
@markus-96 Thanks for the comments. I realized that I have not focused my problem correctly. It is not so much the
I will improve my post to focus only on |
@eyllanesc-JE if you move
Does it fix the issue? |
@henadzit No, I get the same error. |
the following is working, but a little bit ugly, but it should be more compliant to PEP8 (imports to the top of the file): from typing import TYPE_CHECKING
import tortoise
from tortoise import fields
from tortoise import Model
from typing_extensions import TypeVar
if TYPE_CHECKING:
from models.foo import Foo
else:
Foo = TypeVar('Foo', bound=Model)
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
) The problem I have with the proposed solution of @eyllanesc-JE is that imports like Also, if you have a structure with multiple apps, you will not be able to reference any models from the other app. ie:
(bar.py and foo.py are the same as in the comments above...) foo2.py [...]
class Foo2(Model):
id = fields.IntField(primary_key=True)
name_s = fields.CharField(max_length=255)
bar: fields.ForeignKeyRelation["Bar"] = fields.ForeignKeyField(
"models.Bar", on_delete=fields.CASCADE, related_name='foos2'
) main.py import json
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Tortoise
from models.bar import Bar
app_modules = {'models': ['models.foo', 'models.bar'], 'models2': ['models2.foo2']}
for name, modules in app_modules.items():
Tortoise.init_models(modules, name, _init_relations=False)
for name, modules in app_modules.items():
Tortoise.init_models(modules, name)
BarIn = pydantic_model_creator(Bar, name="BarIn")
print(json.dumps(BarIn.model_json_schema(), indent=4)) So, I think the ugly way of "importing" stuff is the most reliable one. Full examplemodels/bar.py from typing import TYPE_CHECKING
import tortoise
from tortoise import fields
from tortoise import Model
if TYPE_CHECKING:
from models.foo import Foo
from models2.foo2 import Foo2
else:
Foo = TypeVar('Foo', bound=Model)
Foo2 = TypeVar('Foo2', bound=Model)
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
)
foo2: fields.ForeignKeyRelation["Foo2"] = fields.ForeignKeyField(
"models2.Foo2", on_delete=fields.CASCADE
) models/foo.py from typing import TYPE_CHECKING
import tortoise
from tortoise import fields
from tortoise import Model
if TYPE_CHECKING:
from models.bar import Bar
else:
Bar = TypeVar('Bar', bound=Model)
class Foo(Model):
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=255)
bar: fields.ForeignKeyRelation["Bar"] = fields.ForeignKeyField(
"models.Bar", on_delete=fields.CASCADE
) models2/foo2.py from typing import TYPE_CHECKING
import tortoise
from tortoise import fields
from tortoise import Model
if TYPE_CHECKING:
from models.bar import Bar
else:
Bar = TypeVar('Bar', bound=Model)
class Foo2(Model):
id = fields.IntField(primary_key=True)
name_s = fields.CharField(max_length=255)
bar: fields.ForeignKeyRelation["Bar"] = fields.ForeignKeyField(
"models.Bar", on_delete=fields.CASCADE, related_name='foos2'
) main.py import json
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Tortoise
from models.bar import Bar
app_modules = {'models': ['models.foo', 'models.bar'], 'models2': ['models2.foo2']}
for name, modules in app_modules.items():
Tortoise.init_models(modules, name, _init_relations=False)
for name, modules in app_modules.items():
Tortoise.init_models(modules, name)
BarIn = pydantic_model_creator(Bar, name="BarIn")
print(json.dumps(BarIn.model_json_schema(), indent=4)) (Edit: simpler main.py) |
Describe the bug
If I use
Forward references
in the same file as I useReverseRelation
this does not generate problems.Output:
But if I separate them into different files:
foo.py
bar.py
main.py
Getting the following:
The text was updated successfully, but these errors were encountered: