You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SQLAlchemy与alchemy-mock中Mock多表关联的跨表过滤查询?

解决mock-alchemy跨表过滤查询返回None的问题

问题背景

现有如下PostgreSQL Schema:

CREATE SCHEMA problem AUTHORIZATION blockchain;

CREATE TABLE problem.users (
    id serial PRIMARY KEY NOT NULL,
    nick varchar NOT NULL
);

CREATE TABLE problem.passwords_keys (
    id serial PRIMARY KEY NOT NULL,
    user_id serial references problem.users,
    password varchar NOT NULL,
    valid bool NOT null DEFAULT TRUE
);

通过sqlacodegen生成SQLAlchemy模型后,想要模拟执行带跨表过滤的查询:

SELECT p."password" "password" FROM 
    problem.passwords_keys p 
    JOIN problem.users u ON(p.user_id=u.id) 
    WHERE p."valid" = true AND u.nick = 'Bush';

使用UnifiedAlchemyMagicMock编写的mock代码在添加User.nick过滤条件后,返回None并报错AttributeError: 'NoneType' object has no attribute 'password',但去掉跨表过滤后可以正常运行。

原因分析

mock-alchemy的UnifiedAlchemyMagicMock对跨表字段的filter条件匹配要求严格,手动定义的mock.call.filter(schema.User.nick == nick)无法和实际查询生成的SQLAlchemy表达式完全匹配,导致找不到对应的mock数据。另外,手动维护调用序列的方式灵活性差,不如让mock自动处理过滤逻辑可靠。

解决方案

方案1:预先注入关联测试数据,让mock自动处理过滤

无需手动定义查询调用序列,直接向mock session添加关联的User和PasswordsKey实例,mock-alchemy会自动处理join和filter逻辑:

from mock_alchemy.mocking import UnifiedAlchemyMagicMock
import schema

nick = 'Bush'

# 创建mock session
session = UnifiedAlchemyMagicMock()

# 构建关联的测试数据
test_user = schema.User(id=22, nick=nick)
test_password = schema.PasswordsKey(id=2, user_id=22, password='qwerty1', valid=True)

# 将数据加入mock session
session.add(test_user)
session.add(test_password)

# 执行目标查询
keys_for_user = session.query(schema.PasswordsKey)\
    .join(schema.User)\
    .filter(schema.PasswordsKey.valid == True)\
    .filter(schema.User.nick == nick)

password = keys_for_user.first().password
print(f'1. Found private key {password} by nick "{nick}"')

方案2:调整mock call的条件匹配方式(不推荐)

如果必须手动定义call序列,需确保filter条件和实际查询生成的表达式完全一致:

from mock_alchemy.mocking import UnifiedAlchemyMagicMock
from unittest import mock
import schema

nick = 'Bush'

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            mock.call.filter(schema.PasswordsKey.valid == True),
            mock.call.filter(schema.User.nick == nick),
        ],
        [schema.PasswordsKey(id=2, user_id=22, password='qwerty1', valid=True)]
    ),
])

keys_for_user = session.query(schema.PasswordsKey)\
    .join(schema.User)\
    .filter(schema.PasswordsKey.valid == True)\
    .filter(schema.User.nick == nick)

password = keys_for_user.first().password
print(f'1. Found private key {password} by nick "{nick}"')

说明

方案1是更推荐的实现方式,它模拟了真实的数据库查询逻辑,无需手动维护调用序列,当查询逻辑变化时,mock代码不需要同步修改,测试更健壮。

内容的提问来源于stack exchange,提问作者baziorek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 01:46:30