重新载入后模型准确率降低_Django ORM Cookbook精选摘录(下) 保持不同模型数据同步更新,使用信号和重写save方法哪个好?...

本文探讨了在Django项目中如何保持不同模型数据同步更新,通过对比使用模型信号(signals)和重写save方法。举例介绍了在创建Hero和Villain模型时如何更新Category模型的hero_count和villain_count字段。作者建议,当字段依赖可控模型时,推荐使用重写.save方法,而依赖第三方应用模型时则使用信号。
摘要由CSDN通过智能技术生成

Django ORM Cookbook是一本不错的关于Django进阶的英文书籍,里面回答了很多我们在利用Django开发Web APP过程中经常遇到的问题,还有许多值得推荐的技巧。这是下篇,上篇文章见Django ORM Cookbook精选摘录(上)。我们着重分享下在Django项目中保持不同模型数据同步更新,使用信号signals和重写save方法哪个更好?

cf3481d6ad786c2318f2802d282265ae.png

1. 使用refresh_from_db重新载入一个字段更改后过的对象

由于Django对queryset自带缓存的属性,当你对一个对象或很多个对象的某些字段进行更新后,你还需要使用refresh_from_db()载入字段更改过后的对象或查询集,要不然内存里还是老的数据。

class TestORM(TestCase):def test_update_result(self):userobject = User.objects.create(username='testuser', first_name='Test', last_name='user')User.objects.filter(username='testuser').update(username='test1user')# At this point userobject.val is still testuser, but the value in the database# was updated to test1user. The object's updated value needs to be reloaded# from the database.userobject.refresh_from_db()self.assertEqual(userobject.username, 'test1user')

2. 如何根据现有数据库转化为Django模型

如果你的现有数据库已经有了很多数据表,希望根据这些数据表生成Django模型,首先你应该在settings.py配置好数据库信息,然后使用如下命令:

$ python manage.py inspectdb > models.py

输入的文件夹会位于当前工作目录,你只需将生成的模型文件移入每个app对应的文件夹即可。

3. 保持不同模型数据同步更新

假如你有如下3个模型,类别(Category), 英雄(Hero)和坏人(Villain), 你希望统计每个类别下英雄和坏人的数量,并保持这个数据实时更新。比如每次创建一个英雄时,对应category模型的hero_count也增加1。我们来看看应该如何实现。

class Category(models.Model):name = models.CharField(max_length=100)hero_count = models.PositiveIntegerField()villain_count = models.PositiveIntegerField()class Meta:verbose_name_plural = "Categories"class Hero(models.Model):name = models.CharField(max_length=100)category = models.ForeignKey(Category, on_delete=models.CASCADE)# ...class Villain(models.Model):name = models.CharField(max_length=100)category = models.ForeignKey(Category, on_delete=models.CASCADE)# ...

这时你可以这么做,重写save()方法,同步更新Category模型数据。

class Hero(models.Model):# ...def save(self, *args, **kwargs):if not self.pk:Category.objects.filter(pk=self.category_id).update(hero_count=F('hero_count')+1)super().save(*args, **kwargs)class Villain(models.Model):# ...def save(self, *args, **kwargs):if not self.pk:Category.objects.filter(pk=self.category_id).update(villain_count=F('villain_count')+1)super().save(*args, **kwargs)

注意我们并没有使用 self.category.hero_count += 1, 而是使用了 update 和F方法来更新字段。这样做的好处是减少数据查询次数,节省内存。如果使用self.category.hero_count方法,我们至少需要进行两次数据库操作,先从数据库读category对象,载入内存,更新字段,然后在写入数据库。而我们使用update和F方法时,我们只需要对数据库进行一次写入操作即可,同时避免将整个category对象载入内存。

一个替代方法是使用Django信号 signals,比如:

from django.db.models.signals import pre_savefrom django.dispatch import receiver@receiver(pre_save, sender=Hero, dispatch_uid="update_hero_count")def update_hero_count(sender, **kwargs):hero = kwargs['instance']if hero.pk:Category.objects.filter(pk=hero.category_id).update(hero_count=F('hero_count')+1)@receiver(pre_save, sender=Villain, dispatch_uid="update_villain_count")def update_villain_count(sender, **kwargs):villain = kwargs['instance']if villain.pk:Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1)

Django项目中经常使用到信号,但使用信号的代码更难阅读和维护。在保持不同模型数据同步更新时,到底使用信号signals,还是重写save方法更好?作者也给出了建议:

  • 当你的字段依赖于一个你可以控制的模型,推荐使用重写 .save

  • 如果你的字段依赖于一个你不能控制第三方app的模型,使用信号。

后续好文,欢迎关注。

大江狗

2019.12.14

93b4131a5af21b69f9d5a061c2a121a3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值