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

基于PostgreSQL+asyncpg的Telegram Bot查询报错:'NoneType'无'send'属性

PostgreSQL+asyncpg Telegram Bot订阅查询异常排查

我开发了一个基于PostgreSQL和asyncpg的Telegram Bot,数据库新增操作正常,但实现订阅查询功能时出现异常。

订阅新增函数

async def add_subscription(user_id: BigInteger, start_date: DateTime, end_date: DateTime):
    async with async_session() as session:
        async with session.begin():
            new_subscription = UserSubscription(
                user_id=user_id,
                subscription_start=start_date,
                subscription_end=end_date
            )
            session.add(new_subscription)
            await session.commit()

订阅查询函数

async def get_subscription(user_id: BigInteger):
    async with async_session() as session:
        print(f"Running DB query for user_id: {user_id}")
        async with session.begin():
            print("session begins")
            try:
                query = text("SELECT subscription_start, subscription_end FROM users WHERE user_id = :user_id")
                result = await session.scalars(query, {"user_id": user_id})
                subscription = result._fetchone_impl()
                return subscription
            except Exception as e:
                print(f"Error during DB query execution: {e}")
                return None

执行报错日志

session begins
2024-09-04 01:08:52,132 INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
2024-09-04 01:08:52,133 INFO sqlalchemy.engine.Engine SELECT subscription_start, subscription_end FROM users WHERE user_id = $1
INFO:sqlalchemy.engine.Engine:SELECT subscription_start, subscription_end FROM users WHERE user_id = $1
2024-09-04 01:08:52,134 INFO sqlalchemy.engine.Engine [cached since 21.7s ago] (1867377574,)
INFO:sqlalchemy.engine.Engine:[cached since 21.7s ago] (1867377574,)
Error during DB query execution: 'NoneType' object has no attribute 'send'
2024-09-04 01:08:52,135 INFO sqlalchemy.engine.Engine COMMIT
INFO:sqlalchemy.engine.Engine:COMMIT
Subscription: None

Bot中实现了/subscription命令,调用查询函数后返回“未找到您的订阅信息”,同时触发上述报错。

测试逻辑

async def main():
    
    # 获取订阅
    subscription = await get_subscription(user_id=1867377574)
    print(subscription)

# 运行主异步循环
asyncio.run(main())

问题分析与解决

核心问题1:调用SQLAlchemy内部私有方法

查询函数中使用了result._fetchone_impl(),这是SQLAlchemy的私有内部方法,不属于公开API范畴。当查询结果为空时,内部游标对象可能为None,调用该方法会触发'NoneType' object has no attribute 'send'错误。

核心问题2:表名可能不匹配

新增操作使用的是UserSubscription模型,但查询SQL中指定的表是users。如果UserSubscription对应的数据库表不是users(通常SQLAlchemy会默认生成user_subscription或user_subscriptions这类表名),会导致查询不到数据。

优化后的查询函数(方案一:修正原生SQL查询)

async def get_subscription(user_id: BigInteger):
    async with async_session() as session:
        print(f"Running DB query for user_id: {user_id}")
        async with session.begin():
            print("session begins")
            try:
                # 替换为UserSubscription对应的真实表名,比如user_subscriptions
                query = text("SELECT subscription_start, subscription_end FROM user_subscriptions WHERE user_id = :user_id")
                result = await session.scalars(query, {"user_id": user_id})
                # 使用公开API first()获取单行结果,自动处理空结果
                subscription = result.first()
                return subscription
            except Exception as e:
                print(f"Error during DB query execution: {e}")
                return None

优化后的查询函数(方案二:使用ORM查询更可靠)

推荐直接使用SQLAlchemy ORM查询,避免手写SQL的表名、字段名错误:

async def get_subscription(user_id: BigInteger):
    async with async_session() as session:
        print(f"Running DB query for user_id: {user_id}")
        async with session.begin():
            print("session begins")
            try:
                # 直接通过模型查询,自动对应表和字段
                subscription = await session.get(UserSubscription, user_id)
                # 按需返回指定字段
                if subscription:
                    return (subscription.subscription_start, subscription.subscription_end)
                return None
            except Exception as e:
                print(f"Error during DB query execution: {e}")
                return None

额外优化:新增函数的冗余代码

新增函数中的await session.commit()是冗余的,async with session.begin()上下文管理器会自动处理事务提交/回滚,可删除该语句:

async def add_subscription(user_id: BigInteger, start_date: DateTime, end_date: DateTime):
    async with async_session() as session:
        async with session.begin():
            new_subscription = UserSubscription(
                user_id=user_id,
                subscription_start=start_date,
                subscription_end=end_date
            )
            session.add(new_subscription)
            # 移除冗余的commit
            # await session.commit()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:55:11