tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.5k stars 368 forks source link

ReverseRelation["Model"] #709

Open jcf-dev opened 3 years ago

jcf-dev commented 3 years ago

Hi all, for Tortoise-ORM how can add the ReverseRelation["Model"] in a multiple file setup without a circular import problem?

long2ice commented 3 years ago
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    # import ...
jcf-dev commented 3 years ago
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    # import ...

I've tried this still getting an error. NameError: name 'Model' is not defined

long2ice commented 3 years ago

Could you show full code?

jcf-dev commented 3 years ago

models/devices.py

from typing import TYPE_CHECKING

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

from models.staff import StaffAccount
from models.relations import resolve_relationships

if TYPE_CHECKING:
    from models.customers import CustomerLogin

class DeviceClassification(Model):
    device_token = fields.UUIDField(unique=True)
    classification = fields.CharField(30)
    valid_from = fields.DatetimeField()
    valid_to = fields.DatetimeField(null=True)
    note = fields.TextField(null=True)

    customer_login: fields.ReverseRelation['CustomerLogin']

resolve_relationships()

DeviceClassification_Pydantic = pydantic_model_creator(DeviceClassification)
DeviceClassification_PydanticIn = pydantic_model_creator(DeviceClassification, exclude_readonly=True)

models/customers.py

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

from models.events import ShardID, SequenceNum
from models.users import UserID
from models.devices import DeviceClassification
from models.relations import resolve_relationships

class CustomerLogin(Model):
    device_token: fields.ForeignKeyNullableRelation[DeviceClassification] = \
        fields.ForeignKeyField(model_name='models.DeviceClassification',
                               related_name='customerlogin_deviceclass',
                               to_field='device_token',
                               on_delete=fields.RESTRICT,
                               null=True)
    event_timedate = fields.DatetimeField(null=True)
    user_ip = fields.CharField(256, null=True)
    user_agent = fields.CharField(1024, null=True)
    client_id = fields.CharField(256, null=True)
    process = fields.CharField(256, null=True)
    auth_result = fields.CharField(256, null=True)
    auth_fail_cause = fields.CharField(256, null=True)
    plain_email = fields.CharField(256, null=True)

resolve_relationships()

CustomerLogin_Pydantic = pydantic_model_creator(CustomerLogin, name='CustomerLogin')
CustomerLogin_PydanticIn = pydantic_model_creator(CustomerLogin, name='CustomerLoginIn', exclude_readonly=True)

models/relations.py

from tortoise import Tortoise

model_list = [
    'models.customers',
    'models.devices',
]

def resolve_relationships() -> None:
    Tortoise.init_models(model_list, "models")

I am getting this error:

NameError: name 'CustomerLogin' is not defined

saintlyzero commented 3 years ago

I faced the same issue of circular imports
The only two solutions that I found were:

  1. Club both CustomerLogin and DeviceClassification into a single file
  2. Remove the reverse relation customer_login: fields.ReverseRelation['CustomerLogin'] from DeviceClassification

Also, I was wondering why do you call resolve_relationships() twice? @joweenflores

jcf-dev commented 3 years ago

I faced the same issue of circular imports

The only two solutions that I found were:

  1. Club both CustomerLogin and DeviceClassification into a single file
  2. Remove the reverse relation customer_login: fields.ReverseRelation['CustomerLogin'] from DeviceClassification

Also, I was wondering why do you call resolve_relationships() twice? @joweenflores

is there really no solution on this with multiple files?

saintlyzero commented 3 years ago

I faced the same issue of circular imports The only two solutions that I found were:

  1. Club both CustomerLogin and DeviceClassification into a single file
  2. Remove the reverse relation customer_login: fields.ReverseRelation['CustomerLogin'] from DeviceClassification

Also, I was wondering why do you call resolve_relationships() twice? @joweenflores

is there really no solution on this with multiple files?

Is there any other possible solution? Please help us @grigi @long2ice

aeweda commented 3 years ago

+1 here, using aerich with type hinting is an absolute mess as well

xulei890817 commented 3 years ago

+1 here, using aerich with type hinting is an absolute mess as well

not work ,the same problem

SnowSuno commented 2 years ago

I came up with a dirty-patching solution:

from __future__ import annotations

class DeviceClassification(Model):
    ...
    customer_login: fields.ReverseRelation['CustomerLogin']

...
from models.customers import CustomerLogin   # import at end of file

Doesn't seem that nice, but it works at least.

markkkkas commented 2 years ago

@SnowSuno what python version are you using? I'm using 3.10 and getting NameError: name <...> is not defined

SnowSuno commented 2 years ago

@SnowSuno what python version are you using? I'm using 3.10 and getting NameError: name <...> is not defined

I'm using 3.9 and 3.10.

In 3.10, it should work even without adding

from __future__ import annotations

since it is supported in default in version 3.10 and above.

c.f. In my case, the NameError I was getting was raised by pydantic_model_creator and was solved by the solution above, which might not be the case in yours.

markkkkas commented 2 years ago

@SnowSuno FYI in 3.10 version that import feature got delayed. So it probably will be in 3.11

marcosgeo commented 2 years ago

I have the same problem, models in multiple files, and thanks to the information above I resolve the issue put from __future__ import annotations. With this, I could use fields.ReverseRelation. I'm using python 3.8.10

vinicius-oa commented 1 year ago
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    ...

The above snippet works in Python 3.10.12

avalanche-tm commented 8 months ago

Any solution for Python 3.11.6? None of the above works