一个非常实用的小方法
试想一下,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踩坑详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]