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发送邮件示例(支持中文邮件标题)
Feb 16 Python
常见的python正则用法实例讲解
Jun 21 Python
详解Python pygame安装过程笔记
Jun 05 Python
Python虚拟环境项目实例
Nov 20 Python
python编写微信远程控制电脑的程序
Jan 05 Python
Python实现八皇后问题示例代码
Dec 09 Python
Python Unittest根据不同测试环境跳过用例的方法
Dec 16 Python
python NumPy ndarray二维数组 按照行列求平均实例
Nov 26 Python
django的模型类管理器——数据库操作的封装详解
Apr 01 Python
Django的ListView超详细用法(含分页paginate)
May 21 Python
PyCharm 2020.2.2 x64 下载并安装的详细教程
Oct 15 Python
python实现数学模型(插值、拟合和微分方程)
Nov 13 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
全国FM电台频率大全 - 7 吉林省
2020/03/11 无线电
Erlang的运算符(比较运算符,数值运算符,移位运算符,逻辑运算符)
2012/07/23 PHP
叫你如何修改Nginx与PHP的文件上传大小限制
2014/09/10 PHP
PHP引用的调用方法分析
2016/04/25 PHP
jquery垂直公告滚动实现代码
2013/12/08 Javascript
使用js操作css实现js改变背景图片示例
2014/03/10 Javascript
node.js中的console.assert方法使用说明
2014/12/10 Javascript
jQuery+Pdo编写login登陆界面
2016/08/01 Javascript
vue.js通过自定义指令实现数据拉取更新的实现方法
2016/10/18 Javascript
jQuery实现简单日期格式化功能示例
2017/09/19 jQuery
微信小程序实现给嵌套template模板传递数据的方式总结
2017/12/18 Javascript
全新打包工具parcel零配置vue开发脚手架
2018/01/11 Javascript
jquery 输入框查找关键字并提亮颜色的实例代码
2018/01/23 jQuery
小程序开发基础之view视图容器
2018/08/21 Javascript
记React connect的几种写法(小结)
2018/09/18 Javascript
基于JavaScript实现简单的轮播图
2021/03/03 Javascript
[02:40]DOTA2英雄基础教程 炼金术士
2013/12/23 DOTA
python调用cmd命令行制作刷博器
2014/01/13 Python
python+django快速实现文件上传
2016/10/24 Python
windows下python之mysqldb模块安装方法
2017/09/07 Python
Python输出各行命令详解
2018/02/01 Python
Python玩转加密的技巧【推荐】
2019/05/13 Python
通过python改变图片特定区域的颜色详解
2019/07/15 Python
浅谈Python 钉钉报警必备知识系统讲解
2020/08/17 Python
CSS3教程(10):CSS3 HSL声明设置颜色
2009/04/02 HTML / CSS
Oakley官网:运动太阳镜、雪镜和服装
2016/09/30 全球购物
在对linux系统分区进行格式化时需要对磁盘簇(或i节点密度)的大小进行选择,请说明选择的原则
2012/11/24 面试题
2013年入党人员的自我鉴定
2013/10/25 职场文书
电子专业推荐信范文
2013/11/18 职场文书
劳动模范事迹材料
2014/01/19 职场文书
本科毕业生求职自荐信
2014/02/03 职场文书
火车的故事教学反思
2014/02/11 职场文书
小学教师师德师风个人整改措施
2014/09/18 职场文书
财务会计岗位职责
2015/02/03 职场文书
公司财务部岗位职责
2015/04/14 职场文书
2016年清明节红领巾广播稿
2015/12/17 职场文书