tortoise-orm icon indicating copy to clipboard operation
tortoise-orm copied to clipboard

ReverseRelation["Model"]

Open jcf-dev opened this issue 3 years ago • 16 comments

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

jcf-dev avatar Apr 09 '21 00:04 jcf-dev

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    # import ...

long2ice avatar Apr 09 '21 01:04 long2ice

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    # import ...

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

jcf-dev avatar Apr 09 '21 01:04 jcf-dev

Could you show full code?

long2ice avatar Apr 09 '21 02:04 long2ice

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

jcf-dev avatar Apr 09 '21 02:04 jcf-dev

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

saintlyzero avatar Apr 09 '21 20:04 saintlyzero

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?

jcf-dev avatar Apr 12 '21 11:04 jcf-dev

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

saintlyzero avatar Apr 12 '21 17:04 saintlyzero

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

aeweda avatar Apr 21 '21 19:04 aeweda

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

not work ,the same problem

xulei890817 avatar Jul 25 '21 15:07 xulei890817

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.

SnowSuno avatar Jan 05 '22 06:01 SnowSuno

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

markkkkas avatar Feb 20 '22 18:02 markkkkas

@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.

SnowSuno avatar Feb 21 '22 00:02 SnowSuno

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

markkkkas avatar Feb 21 '22 21:02 markkkkas

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

marcosgeo avatar Mar 12 '22 19:03 marcosgeo

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    ...
    

The above snippet works in Python 3.10.12

vinicius-oa avatar Aug 11 '23 18:08 vinicius-oa

Any solution for Python 3.11.6? None of the above works

avalanche-tm avatar Dec 15 '23 18:12 avalanche-tm