如何为Django Form ChoiceField传递company_id参数实现数据库过滤查询
Django Form动态传递company_id过滤ChoiceField选项的解决方案
你的现有代码存在两个核心问题:
- 数据库查询写在类顶层,属于类属性初始化逻辑,会在类加载时执行,此时
company_id还未通过__init__传入,根本无法获取到正确的参数; - 使用字符串拼接SQL语句,存在SQL注入风险,非常不安全。
以下是修正后的完整实现:
修正后的forms.py
from django import forms import db class NewColumn(forms.Form): # 先定义ChoiceField,初始设置空选项 engine_choices = forms.ChoiceField( choices=[], help_text='Make your Choice' ) def __init__(self, *args, **kwargs): # 取出传入的company_id,再调用父类初始化方法 company_id = kwargs.pop('company_id') super().__init__(*args, **kwargs) # 在实例化阶段执行数据库查询,此时能获取到正确的company_id engine = db.engine("Database") # 使用with语句自动管理连接和游标,避免资源泄漏 with engine.raw_connection() as connection: with connection.cursor() as cursor: # 用参数化查询替代字符串拼接,彻底避免SQL注入 cursor.execute("SELECT id, name FROM table WHERE id = %s;", (company_id,)) all_rows = cursor.fetchall() # 动态为ChoiceField设置过滤后的选项 self.fields['engine_choices'].choices = [(row[0], row[1]) for row in all_rows]
views.py无需修改
你的views代码已经正确完成了company_id的传递,保持原样即可:
if request.method == 'GET': company_id = 1 site_id = 3 return render(request, 'sites/new-column.html', { 'company_id': company_id, 'site_id': site_id, 'form_newcolumn': NewColumn(company_id), })
关键改动说明
- 查询逻辑移至
__init__方法:类顶层代码仅在类加载时执行一次,而__init__在每次实例化表单时都会运行,确保每次都能根据传入的company_id动态查询数据; - 参数化查询防注入:用占位符替代字符串拼接,将
company_id作为参数传入execute,避免恶意SQL注入; - 自动管理数据库资源:通过
with语句自动关闭游标和连接,避免资源泄漏; - 动态设置字段选项:通过
self.fields['字段名'].choices修改选项,保证每次实例化表单都加载对应公司的专属数据。
可选优化
- 如果查询结果为空,可以设置默认选项,比如
[(None, '暂无可用选项')]; - 建议添加数据库异常捕获,避免因连接失败或查询错误导致表单初始化崩溃。
内容的提问来源于stack exchange,提问作者fox1203
相关产品推荐
相关产品推荐

