小白求教个循环导入的问题

13 小时 53 分钟前
 dylyft

最近刚开始学 python 和 fastapi, 按照 fastapi 教程学习时遇到个问题, 因为使用 sqlmodel 的 relationship, 导致两个模型文件相互导入, 然后报错了, 根据官方文档的解决办法, 使用 TYPE_CHECKING 和字符串版本类型后暂时解决了报错.
但是当接口通过 response_model 指定了数据模型(非表模型)作为返回类型时, 又出现了报错, 报错提示如下:

`TypeAdapter[typing.Annotated[app.models.teams.TeamPublicWithUser, FieldInfo(annotation=TeamPublicWithUser, required=True)]]` is not fully defined; you should define `typing.Annotated[app.models.teams.TeamPublicWithUser, FieldInfo(annotation=TeamPublicWithUser, required=True)]` and all referenced types, then call `.rebuild()` on the instance.

求各位大佬帮忙看看, 哪里有问题. python 版本用的是 3.12, 具体代码如下
models/users.py

from typing import TYPE_CHECKING, Any, Optional

from sqlmodel import Field, Relationship, SQLModel

if TYPE_CHECKING:
    from .teams import Team, TeamPublic


class UserBase(SQLModel):
    username: str = Field(index=True, max_length=255, unique=True)
    email: str | None = Field(default=None, index=True, max_length=255)
    is_active: bool = True
    is_superuser: bool = False
    full_name: str | None = Field(default=None, max_length=255)
    team_id: int | None = Field(default=None, foreign_key="team.id")


class User(UserBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    hashed_password: str
    team: Optional["Team"] = Relationship(back_populates="members")


class UserPublic(UserBase):
    id: int


class UserPublicWithTeam(UserPublic):
    team: Optional["TeamPublic"] = None

models/teams.py

from typing import TYPE_CHECKING, Any, List

from sqlmodel import Field, Relationship, SQLModel

if TYPE_CHECKING:
    from .users import User, UserPublic


class TeamBase(SQLModel):
    name: str = Field(max_length=255)


class Team(TeamBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    members: List["User"] = Relationship(back_populates="team")


class TeamPublic(TeamBase):
    id: int


class TeamPublicWithUser(TeamPublic):
    members: List["UserPublic"] = []

router/users.py

# 用户相关接口
from sqlmodel import and_, select

from app.api.deps import SessionDep
from app.models.users import (
    User,
    UserPublicWithTeam,
)
from app.new_router import new_router

router = new_router(prefix="/users", tags=["用户管理"])


@router.get("/{id}", summary="获取用户详情", response_model=UserPublicWithTeam)
async def get_user_api(db: SessionDep, id: int):
    user = db.exec(select(User).where(and_(User.id == id, User.is_active))).first()
    return user

router/teams.py

# 团队相关接口
from sqlmodel import select

from app.api.deps import SessionDep
from app.models.teams import (
    Team,
    TeamPublicWithUser,
)
from app.new_router import new_router

router = new_router(prefix="/teams", tags=["团队管理"])


@router.get("/{id}", summary="获取团队详情", response_model=TeamPublicWithUser)
async def get_team_api(db: SessionDep, id: int):
    team = db.exec(select(Team).where(Team.id == id)).first()
    return team
534 次点击
所在节点    Python
5 条回复
ylei
9 小时 46 分钟前
.
├── db.py
├── main.py
├── models
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-311.pyc
│ │ ├── teams.cpython-311.pyc
│ │ └── users.cpython-311.pyc
│ ├── teams.py
│ └── users.py
├── __pycache__
│ ├── db.cpython-311.pyc
│ └── main.cpython-311.pyc
├── pyproject.toml
├── README.md
├── router
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-311.pyc
│ │ ├── teams.cpython-311.pyc
│ │ └── users.cpython-311.pyc
│ ├── teams.py
│ └── users.py
├── test.db
└── uv.lock




db.py


from fastapi import Depends
from sqlmodel import Session
from typing import Generator
from sqlmodel import SQLModel, create_engine

DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, echo=True)


def get_session() -> Generator[Session, None, None]:
with Session(engine) as session:
yield session


SessionDep = Depends(get_session)


def create_db_and_tables():
SQLModel.metadata.create_all(engine)


teams.py


from sqlmodel import select
from sqlmodel import Session


from db import SessionDep
from models.teams import (
Team,
TeamPublicWithUser,
)
from fastapi import APIRouter


router = APIRouter(prefix="/teams", tags=["团队管理"])


@router.get("/{id}", summary="获取团队详情", response_model=TeamPublicWithUser)
async def get_team_api(id: int, db: Session = SessionDep, ):
team = db.exec(select(Team).where(Team.id == id)).first()
return team
ylei
9 小时 12 分钟前
我 get 到问题了是 models/teams.py 和 models/users.py 相互引用

我觉得可以从两个方面入手
1. 需求上 users.py 里的表是否可以不依赖 teams
2. 存在外键的表独立出来 users_teams.py?
yingxiangyu
9 小时 9 分钟前
把循环引用的 import 放到 class 里面,不要放到文件开头
hugozach
9 小时 6 分钟前
使用依赖注入设计模式
laminux29
9 小时 5 分钟前
X-Y 问题。

从建模上来说,Team 是由 User 构成,Team 引用 User 是正确的,但 User 为什么要引用 Team ?

这明显是个建模错误,而非循环导入问题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1171662

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX