Django model重写save方法及update踩坑详解


Posted in Python onJuly 27, 2020

一个非常实用的小方法

试想一下,Django中如果我们想对保存进数据库的数据做校验,有哪些实现的方法?

我们可以在view中去处理,每当view接收请求,就对提交的数据做校验,校验不通过直接返回错误,不写数据库,校验通过再调用create或update方法写入数据库

以上方式比较简单,容易理解,但随之又带来了麻烦,我们需在所有接收数据的地方都要去校验,那么有没有更加优雅的方式呢?如果你看过我之前的文章『Django使用Signals监测model字段变化发送通知』]就能想到可以通过signals信号来处理,添加一个pre_save的信号,每当数据库数据变更前都会触发pre_save方法,可以在这里进行校验,免去了view中多个地方校验的麻烦

而今天要说的并不是signals,而是另一种比较常用的做法:重写model的save方法

重写save方法

save方法的主要作用就是将一个对象保存到数据库。如果我们想在数据入库之前做一些处理,除了上边提到的signals之外,还可以通过重写save方法来实现。具体实现方式看下面这个例子

假如我们定义了model如下:

class TempTask(models.Model):
  ...
  
  exechost = models.CharField(max_length=64, default='localhost', verbose_name='执行主机')
  execuser = models.ForeignKey(ExecUser, null=True, on_delete=models.PROTECT, db_constraint=False)

exechost默认为Localhost,execuser默认为空,现有需求:当exechost不为localhost时,他必须符合ip:port的格式,且

execuser不能为空。这是一个比较复杂的校验方式,我们可以通过重写save方法来处理

class TempTask(models.Model):
  ...

  def save(self, *args, **kwargs):
    if self.exechost and (self.exechost.strip() != 'localhost'):
      if len(self.exechost.split(':')) != 2:
        raise ValidationError('执行主机格式错误,应为ip:port格式')

      if not self.execuser:
        raise ValidationError('当执行主机存在时执行用户不能为空')

    super().save(*args, **kwargs)

我们可以在save函数内执行各种自定义逻辑,但需要注意的是,最后必须要调用super().save()方法来保证执行了父类的save(),这样才能保证数据写入了数据库。

这样在当我们执行create语句插入数据的时候就会先去执行save中的校验方法进行校验了

TempTask.objects.create(**postdata)

update踩坑

就当我以为一切都要结束准备起身冲杯咖啡的时候,我发现新加数据可以正常进行校验,但更新数据却不行,更新的代码如下:

TempTask.objects.filter(id=pk).update(**postdata)

经过一番查找发现了问题所在,官方文档中有这么一句话

Unfortunately, there isn't a workaround when creating or updating objects in bulk, since none of save(), pre_save, and post_save are called.

也就是说,当使用查询集批量更新对象时,将不会为每个对象调用save()方法,连pre_save和post_save也不会被调用。与save()类似的还有model的delete()方法,当批量删除的时候,同样不会调用model的delete()方法,但delete是可以使用pre_delete或post_delete信号的

解决这个问题的方法很简单,那就是将更新的代码换成下边这种,保证调用到save方法

_t = TempTask.objects.get(id=pk)
_t.__dict__.update(**postdata)
_t.save()

补充知识:django model save方法对未更改的字段依然进行了保存

看代码吧~

obj = Obj.objects.get(id=1)
print obj.name #此时name的值假定为'abc'
 
def handler(oid):
  obj = Obj.objects.get(id=oid)
  obj.name = '123'
  obj.save()
handler(obj.id)
obj.age = 10
obj.save()
print obj.name

最终的name结果依然为'abc'。save()保存时,虽然没有更改其它字段,但依然会将内存中的值,再次存入数据库,子函数和其它进程更改的值会被覆盖。

以上这篇Django model重写save方法及update踩坑详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python 算法 排序实现快速排序
Jun 05 Python
Python编写生成验证码的脚本的教程
May 04 Python
Python基于pygame实现图片代替鼠标移动效果
Nov 11 Python
Django + Uwsgi + Nginx 实现生产环境部署的方法
Jun 20 Python
Windows下PyCharm安装图文教程
Aug 27 Python
selenium获取当前页面的url、源码、title的方法
Jun 12 Python
python 直接赋值和copy的区别详解
Aug 07 Python
Python Django 命名空间模式的实现
Aug 09 Python
python pycharm的安装及其使用
Oct 11 Python
Python爬取365好书中小说代码实例
Feb 28 Python
基于python 取余问题(%)详解
Jun 03 Python
PyCharm配置KBEngine快速处理代码提示冲突、配置命令问题
Apr 03 Python
matplotlib 画双轴子图无法显示x轴的解决方法
Jul 27 #Python
虚拟机下载python是否需要联网
Jul 27 #Python
详解在Python中使用Torchmoji将文本转换为表情符号
Jul 27 #Python
基于python实现操作git过程代码解析
Jul 27 #Python
2021年的Python 时间轴和即将推出的功能详解
Jul 27 #Python
经验丰富程序员才知道的8种高级Python技巧
Jul 27 #Python
在 Windows 下搭建高效的 django 开发环境的详细教程
Jul 27 #Python
You might like
PHP base64+gzinflate压缩编码和解码代码
2008/10/03 PHP
pdo中使用参数化查询sql
2011/08/11 PHP
PHP生成自定义长度随机字符串的函数分享
2014/05/04 PHP
PHP合并数组+号和array_merge的区别
2015/06/25 PHP
在Windows系统下使用PHP生成Word文档的教程
2015/07/03 PHP
iis 7下安装laravel 5.4环境的方法教程
2017/06/14 PHP
微信企业转账之入口类分装php代码
2018/10/01 PHP
PHP设计模式(九)外观模式Facade实例详解【结构型】
2020/05/02 PHP
JS IE和FF兼容性问题汇总
2009/02/09 Javascript
js TextArea的选中区域处理
2010/12/28 Javascript
javascript使用百度地图api和html5特性获取浏览器位置
2014/01/10 Javascript
JavaScript实现网页截图功能
2014/10/16 Javascript
Jquery插件之Fancybox丰富的弹出层效果附源码下载
2015/12/02 Javascript
prototype.js常用函数详解
2016/06/18 Javascript
Jquery实时监听input value的实例
2017/01/26 Javascript
Vue中的v-cloak使用解读
2017/03/27 Javascript
vue轮播图插件vue-awesome-swiper的使用代码实例
2017/07/10 Javascript
Vue Transition实现类原生组件跳转过渡动画的示例
2017/08/19 Javascript
[原创]微信小程序获取网络类型的方法示例
2019/03/01 Javascript
深入分析在Python模块顶层运行的代码引起的一个Bug
2014/07/04 Python
python分割列表(list)的方法示例
2017/05/07 Python
selenium+python 去除启动的黑色cmd窗口方法
2018/05/22 Python
浅谈Tensorflow由于版本问题出现的几种错误及解决方法
2018/06/13 Python
解决pycharm py文件运行后停止按钮变成了灰色的问题
2018/11/29 Python
如何基于Python + requests实现发送HTTP请求
2020/01/13 Python
Python matplotlib实时画图案例
2020/04/23 Python
Python定时任务框架APScheduler原理及常用代码
2020/10/05 Python
MyHeritage美国:家族史研究和DNA测试的领先服务
2019/05/27 全球购物
Deichmann英国:德国鞋类零售商
2021/01/30 全球购物
new修饰符是起什么作用
2015/06/28 面试题
应用艺术毕业生的自我评价
2013/12/04 职场文书
青春无悔演讲稿
2014/05/08 职场文书
2014年班长个人工作总结
2014/11/14 职场文书
会计工作岗位职责
2015/02/03 职场文书
中小企业员工手册范本
2015/05/14 职场文书
HTML常用标签超详细整理
2022/03/19 HTML / CSS