from __future__ import annotations from datetime import datetime from typing import List, Optional from uuid import UUID, uuid4 from pydantic import EmailStr from sqlmodel import Field, Relationship, SQLModel from sqlalchemy.orm import Mapped, mapped_column # --------------------------- # 用户模型 # --------------------------- class UserBase(SQLModel): email: EmailStr = Field(unique=True, index=True, max_length=255) is_active: bool = True is_superuser: bool = False full_name: Optional[str] = Field(default=None, max_length=255) class UserCreate(UserBase): password: str = Field(min_length=8, max_length=40) class UserRegister(SQLModel): email: EmailStr = Field(max_length=255) password: str = Field(min_length=8, max_length=40) full_name: Optional[str] = Field(default=None, max_length=255) class UserUpdate(SQLModel): email: Optional[EmailStr] = Field(default=None, max_length=255) full_name: Optional[str] = Field(default=None, max_length=255) password: Optional[str] = Field(default=None, min_length=8, max_length=40) class UserUpdateMe(SQLModel): email: Optional[EmailStr] = Field(default=None, max_length=255) full_name: Optional[str] = Field(default=None, max_length=255) class UpdatePassword(SQLModel): current_password: str = Field(min_length=8, max_length=40) new_password: str = Field(min_length=8, max_length=40) class UserBarFollow(SQLModel, table=True): __tablename__ = "user_bar_follow" user_id: UUID = Field(foreign_key="user.id", primary_key=True) bar_id: UUID = Field(foreign_key="bar.bar_id", primary_key=True) created_at: datetime = Field(default_factory=datetime.utcnow) class User(UserBase, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) hashed_password: str = Field(nullable=False) created_posts: List["Post"] = Relationship(back_populates="author") created_replies: List["Reply"] = Relationship(back_populates="author") created_bars: List["Bar"] = Relationship(back_populates="creator") followed_bars: List["Bar"] = Relationship(back_populates="followers", link_model=UserBarFollow) class UserPublic(UserBase): id: UUID class UsersPublic(SQLModel): data: List[UserPublic] count: int # --------------------------- # 吧模型 # --------------------------- class BarBase(SQLModel): name: str = Field(..., min_length=1, max_length=50) description: str = Field(..., min_length=1, max_length=500) follower_count: int = Field(default=0, ge=0) post_count: int = Field(default=0, ge=0) creator_id: UUID is_official: bool = Field(default=False) class BarCreate(BarBase): pass class BarUpdate(SQLModel): name: Optional[str] = Field(None, min_length=1, max_length=255) description: Optional[str] = Field(None, min_length=1, max_length=255) follower_count: Optional[int] = Field(None, ge=0) class Bar(SQLModel, table=True): bar_id: UUID = Field(default_factory=uuid4, primary_key=True) name: str = Field(..., min_length=1, max_length=50) description: str = Field(..., min_length=1, max_length=500) follower_count: int = Field(default=0, ge=0) post_count: int = Field(default=0, ge=0) creator_id: UUID = Field(foreign_key="user.id") is_official: bool = Field(default=False) created_at: datetime = Field(default_factory=datetime.utcnow) creator: "User" = Relationship(back_populates="created_bars") posts: List["Post"] = Relationship(back_populates="bar") followers: List["User"] = Relationship(back_populates="followed_bars", link_model=UserBarFollow) class BarPublic(BarBase): bar_id: UUID # --------------------------- # 帖子模型 # --------------------------- class PostBase(SQLModel): title: str = Field(..., max_length=100) content: str = Field(..., max_length=5000) is_top: bool = Field(default=False) like_count: int = Field(default=0, ge=0) reply_count: int = Field(default=0, ge=0) view_count: int = Field(default=0, ge=0) class PostCreate(PostBase): pass class PostUpdate(SQLModel): title: Optional[str] = Field(None, max_length=100) content: Optional[str] = Field(None, max_length=5000) is_top: Optional[bool] = None class Post(SQLModel, table=True): post_id: UUID = Field(default_factory=uuid4, primary_key=True) bar_id: UUID = Field(foreign_key="bar.bar_id") author_id: UUID = Field(foreign_key="user.id") title: str = Field(..., max_length=100) content: str = Field(..., max_length=5000) created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) bar: "Bar" = Relationship(back_populates="posts") author: "User" = Relationship(back_populates="created_posts") class PostPublic(PostBase): post_id: UUID bar_id: UUID author_id: UUID created_at: datetime updated_at: datetime # --------------------------- # 回复模型 # --------------------------- class ReplyBase(SQLModel): content: str = Field(..., max_length=2000) class ReplyCreate(ReplyBase): pass class ReplyUpdate(SQLModel): content: Optional[str] = Field(None, max_length=2000) class Reply(SQLModel, table=True): reply_id: UUID = Field(default_factory=uuid4, primary_key=True) post_id: UUID = Field(foreign_key="post.post_id") author_id: UUID = Field(foreign_key="user.id") content: str = Field(..., max_length=2000) created_at: datetime = Field(default_factory=datetime.utcnow) post: "Post" = Relationship(back_populates="replies") author: "User" = Relationship(back_populates="created_replies") class ReplyPublic(ReplyBase): reply_id: UUID post_id: UUID author_id: UUID created_at: datetime # --------------------------- # 其他模型 # --------------------------- class Message(SQLModel): message: str class Token(SQLModel): access_token: str token_type: str = "bearer" class TokenPayload(SQLModel): sub: Optional[str] = None class NewPassword(SQLModel): token: str new_password: str = Field(min_length=8, max_length=40) 报错PS C:\Users\林林子\Desktop\full-stack-fastapi-template-0.8.0\backend> docker logs full-stack-fastapi-template-080-prestart-1 + python app/backend_pre_start.py /app/app/core/config.py:105: UserWarning: The value of SECRET_KEY is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of POSTGRES_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of FIRST_SUPERUSER_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) INFO:__main__:Initializing service INFO:__main__:Starting call to '__main__.init', this is the 1st time calling it. INFO:__main__:Service finished initializing + alembic upgrade head /app/app/core/config.py:105: UserWarning: The value of SECRET_KEY is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of POSTGRES_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of FIRST_SUPERUSER_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) INFO [alembic.runtime.migration] Context impl PostgresqlImpl. INFO [alembic.runtime.migration] Will assume transactional DDL. + python app/initial_data.py /app/app/core/config.py:105: UserWarning: The value of SECRET_KEY is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of POSTGRES_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) /app/app/core/config.py:105: UserWarning: The value of FIRST_SUPERUSER_PASSWORD is "changethis", for security, please change it, at least for deployments. warnings.warn(message, stacklevel=1) INFO:__main__:Creating initial data Traceback (most recent call last): File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/clsregistry.py", line 516, in _resolve_name rval = d[token] File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/util/_collections.py", line 345, in __missing__ self[key] = val = self.creator(key) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/clsregistry.py", line 484, in _access_cls return self.fallback[key] KeyError: "List['Post']" The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/app/app/initial_data.py", line 23, in <module> main() File "/app/app/initial_data.py", line 18, in main init() File "/app/app/initial_data.py", line 13, in init init_db(session) File "/app/app/core/db.py", line 24, in init_db user = session.exec( File "/app/.venv/lib/python3.10/site-packages/sqlmodel/orm/session.py", line 66, in exec results = super().execute( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2362, in execute return self._execute_internal( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2247, in _execute_internal result: Result[Any] = compile_state_cls.orm_execute_statement( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 305, in orm_execute_statement result = conn.execute( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1418, in execute return meth( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection return connection._execute_clauseelement( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1632, in _execute_clauseelement compiled_sql, extracted_params, cache_hit = elem._compile_w_cache( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 703, in _compile_w_cache compiled_sql = self._compiler( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 316, in _compiler return dialect.statement_compiler(dialect, self, **kw) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/compiler.py", line 1429, in __init__ Compiled.__init__(self, dialect, statement, **kwargs) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/compiler.py", line 870, in __init__ self.string = self.process(self.statement, **compile_kwargs) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/compiler.py", line 915, in process return obj._compiler_dispatch(self, **kwargs) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/compiler.py", line 4679, in visit_select compile_state = select_stmt._compile_state_factory( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/sql/base.py", line 683, in create_for_statement return klass.create_for_statement(statement, compiler, **kw) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 1110, in create_for_statement _QueryEntity.to_compile_state( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 2565, in to_compile_state _MapperEntity( File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 2645, in __init__ entity._post_inspect File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 1253, in __get__ obj.__dict__[self.__name__] = result = self.fget(obj) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py", line 2711, in _post_inspect self._check_configure() File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py", line 2388, in _check_configure _configure_registries({self.registry}, cascade=True) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py", line 4204, in _configure_registries _do_configure_registries(registries, cascade) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py", line 4245, in _do_configure_registries mapper._post_configure_properties() File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py", line 2405, in _post_configure_properties prop.init() File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/interfaces.py", line 584, in init self.do_init() File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/relationships.py", line 1642, in do_init self._setup_entity() File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/relationships.py", line 1854, in _setup_entity self._clsregistry_resolve_name(argument)(), File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/clsregistry.py", line 520, in _resolve_name self._raise_for_name(name, err) File "/app/.venv/lib/python3.10/site-packages/sqlalchemy/orm/clsregistry.py", line 491, in _raise_for_name raise exc.InvalidRequestError( sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper[User(user)], expression "relationship("List['Post']")" seems to be using a generic class as the argument to relationship(); please state the generic argument using an annotation, e.g. "created_posts: Mapped[List['Post']] = relationship()"
时间: 2025-08-10 16:21:17 浏览: 14
<think>好的,我现在需要帮助用户解决在SQLAlchemy中使用泛型类作为relationship参数时遇到的InvalidRequestError错误。用户提到具体问题是在初始化Mapper时,表达式`relationship("List['Post']")`导致了错误,并且需要明确如何用注解声明泛型参数,例如`created_posts: Mapped[List["Post"]]= relationship()`。首先,我应该回忆一下SQLAlchemy中的relationship函数和类型注解的使用。用户的问题涉及到泛型类型,比如List['Post'],可能在声明关系时没有正确指定,导致ORM无法正确映射。常见的InvalidRequestError通常是因为模型类在relationship中被引用时没有正确解析,尤其是在使用前向引用(forwardreference)或者泛型容器的时候。接下来,我需要考虑SQLAlchemy的版本。用户可能在使用2.0及以上版本,因为新的Mapped注解方式是在2.0引入的。在旧版本中,可能需要不同的处理方式,但用户提供的示例使用了Mapped,所以假设用户使用的是较新的版本。
然后,用户的问题可能出在如何正确注解泛型类型。Python的类型提示中,泛型如List需要使用from__future__import annotations或者在Python3.7+中自动支持,但可能还需要正确导入typing中的泛型或者使用__future__导入。此外,SQLAlchemy的Mapped需要和泛型一起使用,比如Mapped[List["Post"]],这里需要注意字符串形式的类型名称(前向引用)是否正确处理。
另一个可能的问题是,当使用泛型容器如List时,是否需要在relationship中明确指定collection_class,或者是否需要使用sqlalchemy.orm中的relationship参数来指定集合类型。不过,在声明式映射中,通常使用类型注解来替代旧的参数,比如使用Mapped[List[...]]而不需要额外指定collection_class,因为List会被自动处理。
然后,用户提供的错误示例是`relationship("List['Post']")`,这可能是因为在旧版本的SQLAlchemy中,relationship的第一个参数是模型类或可解析的字符串,而直接传递List['Post']会导致无法正确解析。正确的做法应该是在类型注解中使用Mapped[List["Post"]],并在relationship()中留空,让ORM根据注解自动推断。
我需要检查用户是否正确使用了from sqlalchemy.ormimport Mapped和mapped_column,以及是否正确设置了__annotations__。此外,可能还需要使用__future__ importannotations来避免类型解析的问题,或者在模型定义中将Post作为字符串引用,并在后面实际导入。
可能的解决方案包括:1.确保正确导入Mapped和relationship。2.使用字符串前向引用,例如将"Post"放在引号中。3.在relationship()中不传递参数,让类型注解自动处理。4.检查模型类是否正确定义,特别是在使用泛型容器时是否需要额外的配置。例如,用户可能应该将代码写成:
```pythonfrom sqlalchemy.ormimport Mapped,relationshipfrom sqlalchemyimport Column, Integer, ForeignKeyclass User(Base):__tablename__ ='users'id =Column(Integer, primary_key=True)created_posts:Mapped[List["Post"]] =relationship()class Post(Base):__tablename__= 'posts'id= Column(Integer,primary_key=True)user_id =Column(Integer, ForeignKey('users.id'))
```这里需要注意,Post类在User之后定义,所以需要用字符串形式。同时,可能需要使用from __future__import annotations来确保类型注解正确解析。另外,用户是否有正确设置外键关系?比如在Post中定义user_id作为外键,并在User的relationship中是否有正确的back_populates或backref参数。如果存在双向关系,可能需要更明确的设置,但用户的问题主要集中在类型注解上。
总结,用户的问题可能源于在relationship参数中直接使用了泛型类型List['Post'],而正确的做法是使用类型注解Mapped[List["Post"]],并在relationship()中留空参数,或者使用正确的参数如lazy="dynamic"等,同时确保模型类的前向引用正确。</think>在SQLAlchemy 2.0+中使用泛型类作为relationship参数时,需要通过类型注解`Mapped[...]`显式声明集合类型。以下是解决方案:
### 正确使用方式
```python
from __future__ import annotations
from typing import List
from sqlalchemy.orm import Mapped, mapped_column, relationship
class User(Base):
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
created_posts: Mapped[List["Post"]] = relationship(back_populates="author") # 注意泛型参数用双引号包裹
class Post(Base):
__tablename__ = 'posts'
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
author: Mapped["User"] = relationship(back_populates="created_posts") # 双向关系需要字符串引用
```
### 关键要点
1. **泛型类型声明**:必须使用`Mapped[List["Post"]]`格式,其中:
- `List`来自标准库typing
- `"Post"`用双引号包裹实现延迟求值[^1]
2. **双向关系处理**:
```python
relationship(back_populates="created_posts", lazy="selectin") # 推荐添加加载策略
```
3. **错误修复要点**:
- 确保所有模型类在模块中完成定义后才执行`Base.metadata.create_all()`
- 使用`from __future__ import annotations`避免循环引用问题
- 不要直接在relationship参数中写`List["Post"]`,应通过类型注解声明
### 常见错误对照表
| 错误写法 | 正确写法 |
|---------|----------|
| `relationship("List[Post]")` | `Mapped[List["Post"]] = relationship()` |
| `relationship(Post)` | `Mapped["Post"] = relationship()` |
| 未声明双向关系 | 添加`back_populates`参数 |
阅读全文
相关推荐


















