0

0

Django RawQuerySet 参数绑定陷阱:避免混淆内置函数与变量

聖光之護

聖光之護

发布时间:2025-11-08 13:26:18

|

1022人浏览过

|

来源于php中文网

原创

django rawqueryset 参数绑定陷阱:避免混淆内置函数与变量

本文旨在解决 Django `RawQuerySet` 中常见的 `ProgrammingError: "Error binding parameter 1: type 'builtin_function_or_method' is not supported"` 错误。该错误通常源于在参数绑定时,将局部变量名与 Python 内置函数名混淆。文章将深入分析错误原因,提供正确的参数绑定方法,并探讨在 Django 中使用 ORM 替代原生 SQL 查询的更优实践,以提高代码的可读性和维护性。

深入理解 RawQuerySet 参数绑定错误

在使用 Django 的 RawQuerySet 执行原生 SQL 查询时,开发者可能会遇到 ProgrammingError: "Error binding parameter 1: type 'builtin_function_or_method' is not supported" 错误。这个错误通常发生在尝试将参数绑定到 SQL 查询中,但传入的参数类型不正确时。

错误场景示例:

假设在 views.py 中有如下代码片段:

# views.py (原始问题代码示例)
if product.variant != "None":
    variants = Variants.objects.filter(product_id=product.id)
    colors = Variants.objects.filter(product_id=product.id, size_id=variants[0].size_id)
    # 错误发生在这里的参数绑定
    sizes = Variants.objects.raw("SELECT * FROM core_variants WHERE product_id=%s GROUP BY size_id",[id])
    variant = Variants.objects.get(id=variants[0].id)
context = {'sizes': sizes,
            'colors': colors,
            'variant': variant,
          }
return render(request, 'core/product-details.html', context)

当 product-details.html 模板尝试迭代 sizes 变量时,就会触发上述 ProgrammingError:

<!-- product-details.html -->
<select name="size" id="size" class="form-control">
    {% for rs in sizes %}
    <option {% if variant.size_id == rs.size_id %}selected{% endif %} value="{{rs.size_id}}">{{rs.size_id.title}}</option>
    {% endfor %}
</select>

错误原因分析:

问题的核心在于 Variants.objects.raw("...", [id]) 这一行。在 Python 中,id() 是一个内置函数,用于返回对象的“身份标识”。当代码中直接使用 [id] 而没有指定 product.id 时,Python 解释器会将 id 识别为内置的 id() 函数本身,而不是 product 对象的 id 属性值。

数据库驱动程序在尝试将 id() 函数对象(一个可调用类型 builtin_function_or_method)绑定到 SQL 查询的 %s 占位符时,发现其类型不受支持,从而抛出 ProgrammingError。它期望的是一个具体的值(如整数或字符串),而不是一个函数引用。

Article Forge
Article Forge

行业文案AI写作软件,可自动为特定主题或行业生成内容

下载

解决方案:正确绑定参数

要解决此问题,只需确保在 RawQuerySet 的参数列表中传递的是变量的实际值,而不是对内置函数的引用。将 [id] 替换为 [product.id] 即可:

# views.py (修正后的代码)
if product.variant is not None: # 推荐使用 'is not None' 而非 '!= "None"'
    variants = Variants.objects.filter(product_id=product.id)
    colors = Variants.objects.filter(
        product_id=product.id, size_id=variants[0].size_id
    )
    # 修正:使用 product.id 传递产品ID值
    sizes = Variants.objects.raw(
        'SELECT * FROM core_variants WHERE product_id=%s GROUP BY size_id',
        [product.id],
    )
    variant = Variants.objects.get(id=variants[0].id)
context = {
    'sizes': sizes,
    'colors': colors,
    'variant': variant,
}
return render(request, 'core/product-details.html', context)

通过这一修改,product.id 的实际整数值将被正确地绑定到 SQL 查询的 product_id 占位符,从而避免了类型错误。

最佳实践与 ORM 替代方案

尽管 RawQuerySet 在某些复杂场景下非常有用,但在许多情况下,Django ORM 提供了更安全、更易读且通常更高效的替代方案。对于本例中获取按 size_id 分组的 Variants 数据,使用 ORM 可以避免原生 SQL 的潜在陷阱,并更好地利用 Django 的抽象能力。

考虑到模板中 {{rs.size_id.title}} 的用法,rs.size_id 似乎是一个指向 Size 模型的 ForeignKey。因此,获取与产品相关联的所有独特 Size 对象是更符合逻辑的需求。

使用 Django ORM 实现相同功能:

我们可以通过链式查询和 distinct() 方法来获取与特定产品相关的所有独特 Size 对象:

# views.py (使用 ORM 替代 RawQuerySet)
from django.db.models import F

if product.variant is not None:
    # 获取与产品关联的所有变体
    all_variants_for_product = Variants.objects.filter(product_id=product.id)

    # 获取所有独特的尺寸(Size对象),通过 Variants 模型反向查询
    # 假设 Variants 模型有一个 ForeignKey 到 Size 模型,名为 'size' 或 'size_id'
    # 如果 size_id 直接是字段,且需要获取关联的 Size 对象,可以这样:
    # sizes = Size.objects.filter(variants__product_id=product.id).distinct()
    # 如果只是需要 Variants 对象本身,但按 size_id 去重(PostgreSQL特有)
    # sizes = Variants.objects.filter(product_id=product.id).order_by('size_id').distinct('size_id')
    # 对于跨数据库兼容性,如果只是需要唯一的 size_id 值,可以这样:
    distinct_size_ids = all_variants_for_product.values_list('size_id', flat=True).distinct()
    # 然后获取对应的 Size 对象(假设存在 Size 模型)
    # 假设 size_id 是 Variants 模型的一个 ForeignKey 到 Size 模型
    from core.models import Size # 假设 Size 模型在 core 应用中
    sizes = Size.objects.filter(id__in=distinct_size_ids)

    # 获取特定尺寸的颜色变体
    # 假设 all_variants_for_product 不为空
    colors = all_variants_for_product.filter(size_id=all_variants_for_product.first().size_id)

    # 获取第一个变体
    variant = all_variants_for_product.first()

context = {
    'sizes': sizes,       # 现在 sizes 包含 Size 对象
    'colors': colors,
    'variant': variant,
}
return render(request, 'core/product-details.html', context)

注意事项:

  1. product.variant is not None: 在 Python 中,比较对象是否为 None 时,推荐使用 is None 或 is not None,而非 != "None"。
  2. ORM 效率: 使用 ORM 通常能更好地利用数据库索引和连接优化,同时降低 SQL 注入的风险。
  3. 减少数据库查询: 在原始代码中,对 Variants 模型进行了多次查询。可以考虑一次性获取所有相关变体,然后通过 Python 代码进行筛选,或者使用 select_related/prefetch_related 减少 N+1 查询问题。例如,all_variants_for_product = list(Variants.objects.filter(product_id=product.id)) 可以在一次查询中获取所有变体,后续操作直接在内存中的 all_variants_for_product 列表上进行。

总结

在 Django 开发中,RawQuerySet 是一个强大的工具,但使用时需格外小心参数绑定。ProgrammingError: "Error binding parameter 1: type 'builtin_function_or_method' is not supported" 错误是一个常见的陷阱,提醒我们必须区分 Python 内置函数与变量名。优先使用 Django ORM 可以有效避免这类问题,并提升代码的可读性、可维护性和安全性。当确实需要原生 SQL 时,请务必确保参数的类型和值正确无误。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

1133

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

340

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

381

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2152

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

380

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

1683

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

585

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

440

2024.04.29

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 4.9万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号