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 fabric实现远程操作和部署示例
Mar 25 Python
Python3.x中自定义比较函数
Apr 24 Python
python调用fortran模块
Apr 08 Python
python异常和文件处理机制详解
Jul 19 Python
python自动12306抢票软件实现代码
Feb 24 Python
Python代码缩进和测试模块示例详解
May 07 Python
kaggle+mnist实现手写字体识别
Jul 26 Python
python+pyqt5实现KFC点餐收银系统
Jan 24 Python
浅谈Python中eval的强大与危害
Mar 13 Python
Python实现最大子序和的方法示例
Jul 05 Python
matplotlib交互式数据光标实现(mplcursors)
Jan 13 Python
pycharm 实现调试窗口恢复
Feb 05 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获取数组中某元素的位置及array_keys函数应用
2013/01/29 PHP
ThinkPHP文件上传实例教程
2014/08/22 PHP
php实现TCP端口检测的方法
2015/04/01 PHP
PHP读取zip文件的方法示例
2016/11/17 PHP
Laravel框架使用技巧之使用url()全局函数返回前一个页面的地址方法详解
2020/04/06 PHP
论坛里点击别人帖子下面的回复,回复标题变成“回复 24# 的帖子”
2009/06/14 Javascript
JQuery自适应IFrame高度(支持嵌套 兼容IE,ff,safafi,chrome)
2011/03/28 Javascript
Jquery index()方法 获取相应元素索引值
2012/10/12 Javascript
javascript获取选中的文本的方法代码
2013/10/30 Javascript
JavaScript实现标题栏文字轮播效果代码
2015/10/24 Javascript
webpack中引用jquery的简单实现
2016/06/08 Javascript
jQuery UI结合Ajax创建可定制的Web界面
2016/06/22 Javascript
js中class的点击事件没有效果的解决方法
2016/10/13 Javascript
如何使用headjs来管理和异步加载js
2016/11/29 Javascript
JQuery统计input和textarea文字输入数量(代码分享)
2016/12/29 Javascript
swiper插件自定义切换箭头按钮
2017/12/28 Javascript
如何换个角度使用VUE过滤器详解
2019/09/11 Javascript
解决layui弹出层layer的area过大被遮挡的问题
2019/09/21 Javascript
Typescript3.9 常用新特性一览(推荐)
2020/05/14 Javascript
vue router返回到指定的路由的场景分析
2020/11/10 Javascript
[01:18:43]2014 DOTA2华西杯精英邀请赛5 24 iG VS DK
2014/05/25 DOTA
[54:10]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
python比较两个列表是否相等的方法
2015/07/28 Python
Python Socket使用实例
2017/12/18 Python
Python实现自定义函数的5种常见形式分析
2018/06/16 Python
​如何愉快地迁移到 Python 3
2019/04/28 Python
Pandas之ReIndex重新索引的实现
2019/06/25 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
2020/04/14 Python
matplotlib subplot绘制多个子图的方法示例
2020/07/28 Python
Python爬虫教程之利用正则表达式匹配网页内容
2020/12/08 Python
html5 touch事件实现触屏页面上下滑动(一)
2016/03/10 HTML / CSS
日本最大级玩偶手办购物:あみあみ Amiami
2018/04/23 全球购物
Monica Vinader官网:英国轻奢珠宝品牌
2020/02/05 全球购物
尽职尽责村干部自我鉴定
2014/01/23 职场文书
党的作风建设心得体会
2014/10/22 职场文书
导游经典开场白——导游词
2019/04/17 职场文书