[英]How to use aggregate functions on joined tables with filters in sqlalchemy?
[英]How to mock query for joined tables in SQLAlchemy and alchemy-mock when where-statement filters data from multiple tables?
我有架构:
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生成了 SLQAlchemy 类:
export DB_PASSWORD=...
export DB_USER=...
sqlacodegen "postgresql://${DB_USER}:${DB_PASSWORD}@localhost/default_database --schema problem | tee schema.py
# coding: utf-8
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, text
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class User(Base):
__tablename__ = 'users'
__table_args__ = {'schema': 'problem'}
id = Column(Integer, primary_key=True, server_default=text("nextval('problem.users_id_seq'::regclass)"))
nick = Column(String, nullable=False)
class PasswordsKey(Base):
__tablename__ = 'passwords_keys'
__table_args__ = {'schema': 'problem'}
id = Column(Integer, primary_key=True, server_default=text("nextval('problem.passwords_keys_id_seq'::regclass)"))
user_id = Column(ForeignKey('problem.users.id'), nullable=False, server_default=text("nextval('problem.passwords_keys_user_id_seq'::regclass)"))
password = Column(String, nullable=False)
valid = Column(Boolean, nullable=False, server_default=text("true"))
user = relationship('User')
我想模拟以执行查询的那些类:
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';
所以我创建了代码:
from mock_alchemy.mocking import UnifiedAlchemyMagicMock # pip3 install mock-alchemy
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 == True)\
.filter(schema.User.nick == nick)
password = keys_for_user.first().password
print(f'1. Found private key {password} by nick "{nick}"')
但它不起作用:
Traceback (most recent call last):
File "/home/agh/Pulpit/blockchain/watra-ledger/src/stack/mock.py", line 32, in <module>
password = keys_for_user.first().password
AttributeError: 'NoneType' object has no attribute 'password'
有趣的是,当我不使用另一个表进行过滤时 - 一切正常:
from mock_alchemy.mocking import UnifiedAlchemyMagicMock # pip3 install mock-alchemy
from unittest import mock
import schema
session = UnifiedAlchemyMagicMock(data=[
(
[
mock.call.query(schema.PasswordsKey),
mock.call.join(schema.User),
mock.call.filter(schema.PasswordsKey.valid == True),
],
[schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
),
])
keys_for_user = session.query(schema.PasswordsKey)\
.join(schema.User)\
.filter(schema.PasswordsKey.valid == True)
row = keys_for_user.first()
print(f'2. Found valid password "{row.password}"')
结果是:
2. Found valid password "qwerty"
我发现的另一个解决方案不是模拟过滤,但它对我来说看起来很脏(IMO 代码只是让测试通过而不是测试):
from mock_alchemy.mocking import UnifiedAlchemyMagicMock # pip3 install mock-alchemy
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=1, user_id=11, password='qwerty', valid=True)]
),
])
keys_for_user = session.query(schema.PasswordsKey)\
.join(schema.User)\
.filter(schema.PasswordsKey == True)\
.filter(schema.User.nick == nick)
password = keys_for_user.first().password
print(f'3. Found private key {password} by nick "{nick}"')
不幸的是,我需要按用户昵称进行过滤,并且必须有某种方法可以使它正常工作。
我终于发现 - 查询连接表没有问题 - 即使对于多个过滤器, mock.call.filter
也应该被调用一次是个问题。
所以不是:
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=1, user_id=11, password='qwerty', valid=True)]
),
])
我们应该这样做:
session = UnifiedAlchemyMagicMock(data=[
(
[
mock.call.query(schema.PasswordsKey),
mock.call.join(schema.User),
mock.call.filter(schema.PasswordsKey.valid == True,
schema.User.nick == nick),
],
[schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
),
])
链接到官方文档中的示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.