Django项目表单提交修改数据后无法更新数据库问题
问题场景
- 项目环境:Django 4.0.4,superAdmin项目,配置3个MySQL数据库:
default、sales、client - 模型配置:
sa应用下的sa_storesheet模型设置managed = False,映射sales库的all_store表,自定义模型管理器默认查询走sales数据库 - 视图逻辑:加了登录校验的
sa_search视图同时处理两类POST请求:一类是搜索表单提交订单ID,从sales库查询对应amazon_order_id的记录渲染到编辑表单;另一类是编辑表单提交,接收字段参数、查询对应记录赋值后调用save()保存 - 异常表现:搜索功能正常,数据能正常回填到表单输入框,但修改值点Update Data按钮提交后,数据库对应数据无变化
根因定位(按出现概率从高到低排序)
1. 写操作未显式指定目标数据库,默认写入default库
这是Django多数据库场景下最高发的问题:自定义模型管理器里配置的using('sales')只对查询类操作生效,save()、update()这类写操作根本不会继承管理器的数据库配置。如果调用save的时候不手动指定库,Django会默认把数据写到default库对应的连接里——这也是为什么操作完不报错,去sales库查不到变化,数据其实写到default库的同名表里了。
别以为给模型设了
managed = FalseDjango就不会执行写操作,这个参数只是告诉Django不要在迁移的时候帮你创建/删除表,和CRUD操作的路由没有任何关系。
2. 视图内POST逻辑未做分支区分,更新代码压根没执行
很多人图省事,在同一个视图里处理搜索和更新两个POST请求,只判断request.method == 'POST',不加任何请求标识区分。点更新按钮提交的时候,请求参数里只有编辑的字段值,没有搜索接口要的订单ID参数,直接就进了搜索逻辑的分支,后面写的save代码从头到尾没跑到,当然不会更新数据。
3. 表单字段参数不匹配,赋值逻辑没拿到有效值
如果编辑表单的输入框没加对应模型字段的name属性,或者name拼写和后端request.POST.get()里写的字段名对不上,后端拿到的参数都是None。如果赋的空值刚好和数据库里的原值一致,表面看起来就是点了更新没任何反应。
4. 数据库账号权限不足
如果项目配置里连sales库用的MySQL账号只有SELECT查询权限,没有UPDATE权限,部分MySQL驱动版本下不会抛出显性报错,会静默失败,也会出现查询正常、更新不生效的问题。
修复方案
所有非default库的写操作显式指定数据库
不管是查询还是保存,涉及非default库的操作都显式加using('sales'),参考代码:# 错误写法:save()默认走default库 order_obj = sa_storesheet.objects.using('sales').get(amazon_order_id=order_id) order_obj.field1 = request.POST.get('field1') order_obj.save() # 正确写法:save时显式指定目标库 order_obj.save(using='sales') # 用批量更新方法时也要指定using sa_storesheet.objects.using('sales').filter(amazon_order_id=order_id).update( field1 = request.POST.get('field1') )给两类POST请求加明确的分支标识
给搜索按钮和更新按钮分别加不同的name属性,后端根据参数判断走哪个逻辑,参考代码:<!-- 搜索按钮 --> <button type="submit" name="action" value="search">搜索订单</button> <!-- 更新按钮 --> <button type="submit" name="action" value="update">Update Data</button>后端视图逻辑分支参考:
def sa_search(request): if request.method == 'POST': action = request.POST.get('action') if action == 'search': # 走搜索逻辑,查询数据回填表单 order_id = request.POST.get('amazon_order_id') order_obj = sa_storesheet.objects.using('sales').get(amazon_order_id=order_id) return render(request, 'search.html', {'order': order_obj}) elif action == 'update': # 走更新逻辑 order_id = request.POST.get('amazon_order_id') order_obj = sa_storesheet.objects.using('sales').get(amazon_order_id=order_id) # 逐字段赋值 order_obj.field1 = request.POST.get('field1') order_obj.field2 = request.POST.get('field2') order_obj.save(using='sales') return redirect('sa_search') # 提交后重定向避免重复提交校验表单字段配置
确认所有编辑输入框都配置了和模型字段一致的name属性,比如模型字段名是sku,输入框就要写<input type="text" name="sku" value="{{ order.sku }}">,避免后端拿不到参数。校验数据库账号权限
登录MySQL执行show grants for 项目配置的数据库账号@'%';,确认账号对sales库的all_store表有UPDATE权限。
验证步骤
修复后可以先在更新逻辑里加个打印语句,点更新按钮的时候看控制台有没有输出,确认代码走到了save分支;提交后直接连sales库查all_store表的对应记录,确认值更新即可。
内容的提问来源于stack exchange,提问作者masudathakur ratna

