SQLModel如何在内省SQLModel类时查找foreign_key信息?

问题描述 投票:0回答:1

我正在循环遍历 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'
python sqlmodel
1个回答
0
投票

我遇到的问题与循环中模型中的某些字段是

<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}")
© www.soinside.com 2019 - 2024. All rights reserved.