我正在循环遍历 SQLModel 类,提取字段信息,并从详细信息创建 .dbml 格式文件。除了尝试在字段中查找foreign_key 信息之外,这非常有效。这是 SQLModel 类的示例:
class Account(PostgresBase, table=True):
__tablename__ = 'accounts'
id: str = Field(default_factory=lambda: genb58_id(22, "account"),
primary_key=True, index=True, nullable=False)
first_name: str = Field(index=True)
last_name: str = Field(index=True)
username: str = Field(index=True, unique=True)
# ----- Relationships -----
some_activity_records: list["SomeActivity"] = Relationship(
back_populates="account")
class SomeActivity(PostgresBase, table=True):
__tablename__ = 'some_activity_recs'
id: str = Field(default_factory=lambda: genb58_id(22, "activity"),
primary_key=True, index=True, nullable=False)
name: str
type: Union[dict, None] = Field(sa_type=JSON)
fk_account_id: Union[str, None] = Field(
default=None, foreign_key="accounts.id")
things: list[str] = Field(sa_type=ARRAY(String), default=[])
# ----- Relationships -----
account: Union["Account", None] = Relationship(
back_populates="some_activity_records")
如何梳理出SomeActivity fk_account_id字段链接到表+字段的foreign_key信息
accounts.id
?
这是我运行的用于获取字段和关系的脚本:
import inspect
from sqlmodel import SQLModel
import sys
import types
import typing
from typing import get_args
from models import *
# Get all classes from models.py that are subclasses of SQLModel.
models = [obj for name, obj in inspect.getmembers(sys.modules[__name__])
if inspect.isclass(obj) and issubclass(obj, SQLModel) and obj != SQLModel]
# Generate dbml for each model.
dbml_content = ""
for model in models:
# Extract class docstring.
doc = inspect.getdoc(model).replace('\n', ' ')
if doc.startswith("Usage docs"):
doc = None
# Build the table definition.
dbml_content = f"Table {model.__tablename__} {{\n"
dbml_content += f" // {doc}\n" if doc else ""
# Build the field lines.
for key in model.model_fields.keys():
fieldstr = f" {key}"
field_info = model.model_fields[key]
annotation = field_info.annotation
match type(annotation):
case t if t == type:
type_name = annotation.__name__
if type_name == "str":
type_name = "varchar"
opts = ""
if key == "id":
opts = "[pk, unique, not null]"
fieldstr += f" {type_name} {opts}"
case t if t == typing._UnionGenericAlias:
args = get_args(annotation)
arg_name = args[0].__name__
match arg_name:
case "dict":
arg_name = "json"
case "str":
arg_name = "varchar"
fieldstr += f" {arg_name}"
case t if t == types.GenericAlias:
fieldstr += f" varchar[]"
case t if t == typing._LiteralGenericAlias:
args = get_args(annotation)
fieldstr += f" varchar //{args}"
dbml_content += fieldstr + "\n"
dbml_content += "}\n\n"
# Add relationships.
relationships = []
for model in models:
for field in model.model_fields.values():
if 'foreign_key' in field.metadata:
fk = field.metadata['foreign_key']
if fk:
ref_table, ref_field = fk.split('.')
relationships.append(
f'Ref: "{model.__tablename__}"."{field.name}" < "{ref_table}"."{ref_field}"\n')
dbml_content += ''.join(relationships)
print(dbml_content)
“为每个模型生成 dbml”。循环效果很好并创建了适当的 dbml,例如:
Table accounts {
id varchar [pk, unique, not null]
first_name varchar
last_name varchar
username varchar
}
Table some_activity_recs {
id varchar [pk, unique, not null]
name varchar
type json
fk_account_id varchar
things varchar[]
}
但是,当我运行“添加关系”部分时,
field.metadata
始终是一个空列表,即使在检查 fk_account_id
字段时也是如此。
我尝试使用
field.foreign_key
成员,该成员应该存在于 SQLModel FieldInfo 类中,但它给了我以下回溯:
***** model.__name__: Account
***** type(model): <class 'sqlmodel.main.SQLModelMetaclass'>
***** field.foreign_key: PydanticUndefined
Traceback (most recent call last):
File "/soc-api/./soc/dao/scripts/create_dbml.py", line 86, in <module>
print(f"***** field.foreign_key: {field.foreign_key}")
^^^^^^^^^^^^^^^^^
AttributeError: 'FieldInfo' object has no attribute 'foreign_key'
我遇到的问题与循环中模型中的某些字段是
<class sqlmodel.main.FieldInfo>
而其他字段恰好是 <pydantic.fields.FieldInfo>
有关。
答案是在循环中使用这个条件:
if hasattr(field_info, "foreign_key") and isinstance(field_info.foreign_key, str):
print(f"***** field_info.foreign_key: {field_info.foreign_key}")