01_什么是ORM

ORM(Object-Relational Mapping) 表示对象关系映射。

简单的说:ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。

<think>我们正在使用SQLAlchemy的异步ORM,特别是`async_sessionmaker`创建的会话。用户希望了解如何使用`filter_by`进行复杂查询的select操作。在SQLAlchemy ORM中,`filter_by`通常用于基于关键字参数的简单过滤,而`filter`方法则更灵活,支持更复杂的表达式。不过,用户明确提到了`filter_by`,因此我们将重点放在`filter_by`上,同时也会提及更复杂的查询场景。 在异步会话中,我们使用`select`语句来执行查询,然后通过`session.execute`来执行。使用`filter_by`时,我们通常结合`select`和`where`条件,但注意:`filter_by`是`Query` API的一部分,而在1.4版本之后,SQLAlchemy推荐使用2.0风格的查询(即使用`select()`和`session.execute()`)。 然而,在2.0风格中,我们通常使用`select`函数配合`where`方法,而`where`方法中可以使用`filter_by`的替代方式(即直接使用列属性比较)。但是,如果我们想使用类似`filter_by`的语法,可以在`select`之后使用`.filter_by()`,但注意这仅适用于`Query`对象,而不适用于`Select`对象。 因此,在2.0风格中,我们通常这样写: ```python stmt = select(User).where(User.name == 'Alice') ``` 这相当于旧版中的`filter_by(name='Alice')`,但更灵活。 不过,为了满足用户对`filter_by`的需求,我们可以这样:在2.0风格中,我们也可以使用`select`然后结合`filter_by`,但需要先将`select`转换成`Select`对象,然后并没有直接的`filter_by`方法。因此,我们推荐使用`where`子句。 但是,如果我们有多个条件,并且希望使用关键字参数的形式,我们可以使用`kwargs`展开,例如: ```python filters = {'name': 'Alice', 'email': 'alice@example.com'} stmt = select(User).filter_by(**filters) ``` 注意:在2.0风格中,`select`对象并没有`filter_by`方法,但是`select`可以配合`where`,而`where`可以接受多个条件。所以,上述代码在2.0风格中是不正确的。 实际上,在2.0风格中,我们应该这样: ```python stmt = select(User).where(User.name == 'Alice', User.email == 'alice@example.com') ``` 或者,如果我们有一个字典,我们可以构建条件: ```python conditions = [getattr(User, key) == value for key, value in filters.items()] stmt = select(User).where(*conditions) ``` 但是,用户可能希望使用`filter_by`的简洁性。在异步ORM中,我们仍然可以使用旧版的`Query`风格,但是SQLAlchemy官方推荐使用2.0风格。 因此,我们给出两种方式: 1. 使用2.0风格的`select`配合`where`(推荐) 2. 使用旧版的`Query`对象(通过`session.query`,但异步会话不支持,因为异步会话只支持2.0风格) 所以,在异步中,我们只能使用2.0风格。因此,我们使用`where`。 但是,请注意,`filter_by`在2.0风格中不可用,除非我们使用`Session.query`,但异步会话没有`query`方法。所以,我们必须使用`select`。 因此,我们只能使用`where`方法。 下面,我将展示如何在异步会话中使用`select`和`where`来实现类似于`filter_by`的功能,并进行复杂查询。 ### 示例:使用`filter_by`的替代方法(`where`)进行查询 假设我们有一个`User`模型,我们想根据多个条件查询用户,这些条件由关键字参数给出。 ```python from sqlalchemy import select from sqlalchemy.orm import selectinload async def get_users_by_filter(session: AsyncSession, **filters): # 构建条件列表 conditions = [getattr(User, key) == value for key, value in filters.items()] stmt = select(User).where(*conditions) result = await session.execute(stmt) return result.scalars().all() ``` 但是,如果我们的查询更复杂,比如包含关联表的条件,那么我们需要使用连接(join)和关联模型的字段。 ### 复杂查询示例:包含关联表 例如,我们想查询所有写了特定标题文章的用户。 ```python async def get_users_by_post_title(session: AsyncSession, title): stmt = select(User).join(User.posts).where(Post.title == title) result = await session.execute(stmt) return result.scalars().all() ``` 这里,我们使用了`join`来连接`User`和`Post`模型(假设`User`有一个关系`posts`指向`Post`模型),然后在`where`子句中使用了`Post.title`。 ### 使用`filter_by`进行多表查询? 对于多表查询,`filter_by`只能用于当前模型,不能直接用于关联模型。因此,在复杂查询中,我们通常使用`filter`(即`where`)来明确指定关联表的字段。 ### 总结 在SQLAlchemy异步ORM中,使用`async_sessionmaker`创建的会话,我们执行查询的步骤如下: 1. 使用`select`构建查询语句。 2. 使用`where`添加条件(替代`filter_by`)。 3. 对于多表查询,使用`join`来连接表,并在`where`中使用关联表的字段。 4. 使用`session.execute`执行查询。 虽然用户提到了`filter_by`,但在2.0风格的异步查询中,我们推荐使用`where`,因为它更灵活且支持复杂条件。 下面是一个完整的示例,包括异步会话的创建和查询执行: ```python from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker from sqlalchemy import select # 假设已有模型 User 和 Post async_engine = create_async_engine(DATABASE_URL) AsyncSessionLocal = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False) async def main(): async with AsyncSessionLocal() as session: # 简单查询:根据name和email查询用户 stmt = select(User).where(User.name == 'Alice', User.email == 'alice@example.com') result = await session.execute(stmt) users = result.scalars().all() # 复杂查询:查询所有写了特定标题文章的用户 stmt = select(User).join(User.posts).where(Post.title == 'My First Post') result = await session.execute(stmt) users_with_post = result.scalars().all() # 如果我们有一个过滤字典 filters = {'name': 'Bob', 'email': 'bob@example.com'} conditions = [getattr(User, key) == value for key, value in filters.items()] stmt = select(User).where(*conditions) result = await session.execute(stmt) users_by_filters = result.scalars().all() # 输出结果 print(users) print(users_with_post) print(users_by_filters) # 运行 import asyncio asyncio.run(main()) ``` ### 注意事项 - 在构建条件时,确保字典中的键是模型的有效属性,否则`getattr`会抛出异常。 - 对于关联查询,使用`join`明确指定连接关系,避免笛卡尔积。 - 异步查询需要使用`await`等待执行完成。 ### 相关问题 1. 在SQLAlchemy异步ORM中,如何执行分页查询? 2. 如何处理异步查询中的关系加载(如joinedload, selectinload)? 3. 在复杂查询中,如何构建动态的过滤条件(例如,根据用户输入的不同字段进行查询)? 4. 使用异步ORM时,如何避免N+1查询问题? 希望这些信息对您有所帮助!</think>### 使用 SQLAlchemy 异步 ORM 实现带 `filter_by` 的复杂查询 在 SQLAlchemy 异步 ORM 中,`filter_by` 主要用于基于关键字参数的简单过滤。以下是完整实现方案,包含复杂查询场景: #### 1. 基础 `filter_by` 查询 ```python from sqlalchemy import select from sqlalchemy.orm import selectinload async def get_user_by_email(session: AsyncSession, email: str): # 使用 filter_by 进行简单过滤 stmt = select(User).filter_by(email=email) result = await session.execute(stmt) return result.scalar_one_or_none() # 返回单个对象或 None ``` #### 2. 多条件复杂查询 ```python async def get_users_with_complex_filter(session: AsyncSession): # 组合 filter_by 与其他查询方法 stmt = ( select(User) .filter_by(is_active=True) # 使用 filter_by .where(User.created_at > "2023-01-01") # 使用 where 添加复杂条件 .order_by(User.id.desc()) .limit(10) ) result = await session.execute(stmt) return result.scalars().all() # 返回对象列表 ``` #### 3. 关联表查询(带预加载) ```python async def get_user_with_posts(session: AsyncSession, user_id: int): # 关联表查询 + 预加载避免 N+1 问题 stmt = ( select(User) .filter_by(id=user_id) .options(selectinload(User.posts)) # 预加载关联数据 ) result = await session.execute(stmt) return result.scalar_one() ``` #### 4. 动态条件组合 ```python from sqlalchemy import and_, or_ async def search_users(session: AsyncSession, **filters): # 动态构建查询条件 conditions = [] if "name" in filters: conditions.append(User.name.ilike(f"%{filters['name']}%")) if "email" in filters: conditions.append(User.email == filters["email"]) if "min_id" in filters: conditions.append(User.id >= filters["min_id"]) stmt = select(User).filter_by(is_verified=True).where(and_(*conditions)) result = await session.execute(stmt) return result.scalars().all() ``` #### 5. 聚合查询与分组 ```python from sqlalchemy import func async def get_user_post_stats(session: AsyncSession): # 聚合查询 + 分组 stmt = ( select( User.id, User.name, func.count(Post.id).label("post_count"), func.max(Post.created_at).label("latest_post") ) .join(Post, User.id == Post.user_id) .filter_by(user_type="author") # 使用 filter_by .group_by(User.id) .having(func.count(Post.id) > 5) # 过滤分组结果 ) result = await session.execute(stmt) return result.all() # 返回元组列表 ``` ### 关键注意事项 1. **`filter_by` vs `where`** - `filter_by`:适用于简单等值查询(`column=value`) - `where`:支持复杂表达式(不等式、函数、子查询等)[^1] 2. **异步执行模式** 所有查询必须使用 `await session.execute(stmt)` 异步执行 3. **结果处理** - `scalar()`:返回单个值 - `scalar_one()`:确保唯一结果(否则报错) - `scalars().all()`:返回对象列表 - `first()`:获取第一条结果 4. **N+1 查询优化** 使用加载策略避免关联查询问题: ```python .options( selectinload(User.posts), # 适合一对多 joinedload(Profile, innerjoin=True) # 适合一对一 ) ``` ### 完整工作流示例 ```python async def main(): async with AsyncSessionLocal() as session: # 1. 创建测试数据 new_user = User(name="张三", email="zhangsan@example.com", is_active=True) session.add(new_user) await session.commit() # 2. 使用 filter_by 查询 user = await get_user_by_email(session, "zhangsan@example.com") print(f"查询结果: {user.name}") # 3. 复杂查询 active_users = await get_users_with_complex_filter(session) print(f"活跃用户数: {len(active_users)}") # 4. 清理 await session.delete(user) await session.commit() asyncio.run(main()) ``` ### 性能优化建议 1. 使用 `.only()` 限制返回字段 ```python stmt = select(User.id, User.name).filter_by(is_active=True) ``` 2. 分页处理大数据集 ```python stmt = select(User).filter_by(role="admin").offset(20).limit(10) ``` 3. 使用 Core API 处理超大数据集 ```python stmt = select(User.__table__.c.id).where(User.age > 30) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值