0

我有一些相互关联的python类,它们试图模仿graphql模式(模式本身不相关,我在这里发布了基本案例以重现问题)。

GraphQL 架构如下所示:

type User {
  name: String
  orders: [Order]
}

type Order {
  key: String
  user: User
}

从模式设计的角度来看,这个模式没有任何问题,它是一个有效的模式,并且我已经有一个使用这种关系运行的数据库(它只是意味着:一个用户可能有多个订单,一个订单可能只有一个用户创造了它)。

在 python 方面,事情变得有点混乱。

我希望以下代码能够工作:

文件:模型/模型.py

import attr

@attr.s
class Model():
  pass # Model internal workings not relevant to the example

文件:模型/User.py

from typing import List
import attr
from . import Model

@attr.s
class User(Model):
  name: str = 'Name'
  orders: List[Order] = attr.ib(factory=list)

文件:模型/Order.py

import attr
from . import Model

@attr.s
class Order(Model):
  key: str = 'abc'
  user: User = attr.ib(factory=User)

然后我可以做这样的事情:

文件:.py

import models as m
user = m.User.query(name='John', with='orders')
user.name # "John"
user.orders # [m.Order(key='1'), m.Order(key='2'), m.Order(key='3')...]
order = m.Order.query(key='1', with='user')
order.key # "1"
order.user # m.User(name="John")

由于循环依赖(用户需要之前定义的订单类型,以及需要用户的订单),此代码不起作用。

我发现的解决方法是使用 importlib 延迟导入模型:

# current solution:
# using the importlib to import dynamically

from typing import List
import attr
from helpers import convert_to, list_convert_to, 

# Note: "convert_to" receives a class name and returns a function to instantiate it dinamically

@attr.s
class Model():
  pass

@attr.s
class User(Model):
  name: str = 'Name'
  orders: List[Model] = attr.ib(factory=list_convert_to('Order'))

@attr.s
class Order(Model):
  key: str = 'abc'
  user: Model = attr.ib(factory=list_convert_to('User'))

这个解决方案有效,但失去了事先知道字段类型的能力,而且我认为在构建复杂关系时它会更慢(数百个项目,对象有几个层次)。

这就是为什么我正在寻找更好的方法来解决这个问题,有什么想法吗?

4

1 回答 1

2

假设您使用的是 Python 3.7 或更高版本,以下行将使其工作:

from __future__ import annotations

它还允许您在定义类时引用它。例如

class C:
    @classmethod
    def factory(cls) -> C:
        ...

现在工作。

如果您的类在多个文件中定义,并且因此获得循环依赖,则可以使用保护导入

from typing import TYPE_CHECKING

# ...

if TYPE_CHECKING:
    from module import User
于 2020-04-11T11:52:50.107 回答