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

作者:ops-coffee 时间:2021-01-21 08:45:52 

一个非常实用的小方法

试想一下,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()保存时,虽然没有更改其它字段,但依然会将内存中的值,再次存入数据库,子函数和其它进程更改的值会被覆盖。

来源:https://www.cnblogs.com/37Y37/p/12840890.html

标签:Django,model,save,update
0
投稿

猜你喜欢

  • 浅谈redux, koa, express 中间件实现对比解析

    2024-05-02 16:28:05
  • asp清理站点缓存代码

    2008-07-21 12:37:00
  • 如何安装控制器JavaScript生成插件详解

    2024-04-10 10:51:51
  • Dreamweaver初学者常见问题解答

    2007-02-03 11:39:00
  • Python的进程间通信详解

    2021-07-16 16:11:25
  • 简单介绍Python中的floor()方法

    2023-06-08 03:36:21
  • Pycharm中出现ImportError:DLL load failed:找不到指定模块的解决方法

    2021-09-04 06:14:32
  • yolov5中head修改为decouple head详解

    2023-07-02 08:54:08
  • Python OpenCV超详细讲解调整大小与图像操作的实现

    2022-07-09 21:16:27
  • Python3实现对列表按元组指定列进行排序的方法分析

    2022-10-05 18:01:13
  • Mysql 索引 BTree 与 B+Tree 的区别(面试)

    2024-01-13 03:10:32
  • Python正则表达式教程之一:基础篇

    2023-11-13 19:46:02
  • 详解Python if-elif-else知识点

    2022-02-23 16:06:23
  • Python实现自动化处理PDF文件的方法详解

    2024-01-02 07:02:21
  • python源码剖析之PyObject详解

    2023-08-02 14:07:03
  • python boto和boto3操作bucket的示例

    2022-08-05 03:22:49
  • Python实现敏感词过滤的4种方法

    2021-10-01 06:21:08
  • 使用动画实现微信读书的换一批效果(两种方式)

    2023-10-23 14:30:55
  • Django中提供的6种缓存方式详解

    2023-03-24 14:55:49
  • 安装PyTorch的详细过程记录

    2023-05-12 07:50:12
  • asp之家 网络编程 m.aspxhome.com