У меня есть несколько классов Python, которые связаны друг с другом, они пытаются имитировать схему graphql (сама схема не имеет значения, я публикую здесь базовый вариант, чтобы воспроизвести проблему).
Схема GraphQL выглядит так:
type User {
name: String
orders: [Order]
}
type Order {
key: String
user: User
}
С точки зрения дизайна схемы, в этой схеме нет ничего плохого, она действительна, и у меня уже есть база данных, работающая с этими отношениями (это просто означает: у пользователя может быть несколько заказов, у заказа может быть только один пользователь который его создал).
На стороне Python все становится немного запутанным.
Я ожидаю, что следующий код будет работать:
файл: модели/Model.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'))
это решение работает, но теряет возможность заранее знать типы полей, и я думаю, что оно медленнее при построении сложных отношений (сотни элементов с объектами на несколько уровней в глубину).
Вот почему я ищу лучшие способы решения этой проблемы, есть идеи?