V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
dylyft
V2EX  ›  Python

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

  •  
  •   dylyft · 13 小时 53 分钟前 · 535 次点击

    最近刚开始学 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
    
    5 条回复    2025-11-10 16:12:37 +08:00
    ylei
        1
    ylei  
       9 小时 47 分钟前
    .
    ├── 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
        2
    ylei  
       9 小时 12 分钟前
    我 get 到问题了是 models/teams.py 和 models/users.py 相互引用

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

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

    这明显是个建模错误,而非循环导入问题。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   1269 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 17:17 · PVG 01:17 · LAX 09:17 · JFK 12:17
    ♥ Do have faith in what you're doing.