我有一个描述数据模型的模块:
Основные классы:
- ActionType: Таблица-Справочник отслеживаемых действий
- AuditLog: Логирование действий пользователей.
- Assembly: Описание сборочных чертежей.
- Part: Описание деталей.
- AssemblyPart: Связь между сборками и деталями.
- AssemblyAssembly: Связь между сборками и другими сборками.
- Material: Таблица-Справочник материалов.
- Thick: Таблица-Справочник толщин материалов.
- ProgramX5: Программы обработки для станков Х5.
- PartProgramX5: Связь между деталями и программами обработки.
- Role: Таблица-Справочник ролей пользователей.
- User: Пользователи системы.
考虑到这是一个最小的结构,我决定立即将其分为 3 部分:
- модуль с основными моделями (Сборки, детали, пользователи)
- модуль со справочниками (роли, материалы и т.д.)
- модуль связей (все модели, организующие связь многие-ко-многим)
然后我遇到了目录/关系和主模型之间的交叉导入。
这是如何解决的?best practices对SQLAlchemy 的声明式风格感兴趣。
相反,问题是关于“循环”进口而不是“平行”进口。
如果是这种情况,那么: 一个模块(文件)
A无法完成初始化,因为它需要一个依赖模块B,而依赖模块又需要A.这是不可能的,试图解决无法解决的问题也不会成功。类型检查
如果我们谈论导入类型(作为注释),那么这就解决了它
TYPE_CHECKING。该类型不能显式使用,例如创建该类的实例
ins = MyType(),但这并不妨碍您调用其方法并在注释中使用它们。简单地说,解释器中的字符串TYPE_CHECKING将被忽略,但它们对静态分析器是可见的。至于库
SQLModel/SQLAlchemy,我们使用相同的库TYPE_CHECKING,并且还将对符号的引用括在括号中,例如 SQLModel:...就像类名完全未定义时所发生的情况一样:
协议
对于某些辅助类型,您可以将协议类放在单独的文件中。几乎像一个抽象类,但更灵活一点并且更少依赖于其他模块。这与定义接口非常相似:
...然后在任何地方实现这样一个协议类
LoggerImpl(不必继承它),但将协议导入到依赖模块中:当然,这种方法需要能够创建
LoggerImpl并将其传递给依赖模块的方法:协议的工作方式与类型相同
TypeScript:结构类型 - 最主要的是所有属性和方法都在那里,并且作为此类型传递的内容并不重要。肮脏的方法
聚苯乙烯
大多数情况下,这样的方法足以解决循环依赖,但我不知道SQLAlchemy中的最佳实践
TYPE_CHECKING/Interface-Protocol是什么。PPS:有时,在一个文件中保留 1000 行会更容易,而无需发明不必要的文件名,也无需解决循环依赖问题。