在Django 1.10.6与PostgreSQL中动态更新ArrayField文件名列表
解决Django 1.10.6中PostgreSQL ArrayField动态更新文件名列表的问题
首先得指出你当前代码里的几个小问题:
list.append()方法是原地修改列表,它的返回值是None,所以你写的completed_uploads = packageInstance.completed_uploads.append(...)会把这个变量设为None,这完全不是你想要的结果。- 就算你正确修改了内存中的实例列表,直接调用
filter.update()却不传递修改后的列表,数据库根本不会同步这个变更。 - 另外,先取实例再修改保存的方式在高并发场景下容易丢失更新——如果两个请求同时拿到同一个
Package实例,各自修改后保存,后保存的会覆盖前一个的变更。
下面给你两种正确的实现方式,按需选择:
方式一:修改实例后保存(低并发场景适用)
这种方式简单直接,适合没有太多并发更新的场景:
# 获取实例 package_instance = Package.objects.get(id=packageId) # 直接调用append修改列表(注意不要把append的返回值赋值给变量) package_instance.completed_uploads.append(request.data['filepath']) # 只更新修改过的字段,提升效率 package_instance.save(update_fields=['completed_uploads'])
方式二:数据库原子更新(高并发场景推荐)
利用PostgreSQL的array_append函数和Django的F()表达式,直接在数据库层面完成数组追加操作,这是原子性的,完全不用担心并发覆盖的问题:
from django.db.models import F, Func, Value # 直接更新数据库,无需先获取实例 Package.objects.filter(id=packageId).update( completed_uploads=Func(F('completed_uploads'), Value(request.data['filepath']), function='array_append') )
如果你的completed_uploads可能为null(虽然模型里设置了default=list,但还是要考虑极端情况),可以用coalesce把null转为空数组后再追加:
Package.objects.filter(id=packageId).update( completed_uploads=Func( # 把null转为空数组 Func(F('completed_uploads'), Value([]), function='coalesce'), Value(request.data['filepath']), function='array_append' ) )
额外注意事项
- 模型里的
completed_uploads=ArrayField(models.TextField(blank=True), null=True, default=list)配置没问题,default=list会确保新创建的实例默认有一个空列表,你用Package.create(..., completed_uploads=[])初始化也是正确的。 - 建议先对
request.data['filepath']做合法性校验,比如判断是否为空字符串,避免把无效内容添加到数组里。
内容的提问来源于stack exchange,提问作者GeneralBear
相关产品推荐
相关产品推荐

