【问题标题】:Django: Create model object with increased version numberDjango:创建具有增加版本号的模型对象
【发布时间】:2015-08-27 15:45:07
【问题描述】:

我有模型,例如:

class Example(models.Model):
   title = models.SlugField()
   version = models.PositiveSmallIntegerField(default=0)
   content = models.TextField(blank=True)

   class Meta:
       unique_together = (
           ('title', 'version'),
       )

我想在下面实现类似的东西,但以某种方式避免竞争条件并使这个原子:

e = Example(title='test')
e.version = Example.objects.filter(title=e.title).aggregate(Max('version'))['version__max']+1
e.save()

附言。我正在使用 Django 1.8

【问题讨论】:

  • 保存前不能过滤
  • 如果此文档的版本最高,我会添加一个BooleanField 来存储
  • 您的实例从哪里保存?既然你提到了原子事务,我认为你不能用@transaction.atomic 装饰那个函数是有原因的。为了确保理解它,您希望同时请求获得相同的version__max 并因此一个save() 导致由于唯一约束而导致完整性错误可能会导致竞争条件?
  • 我担心计算 version 作为最大可能版本 + 1 和调用 save() 方法之间的时间。我可以在with transaction.atomic(): 中换行第 2 行和第 3 行吗?

标签: python django django-models django-orm


【解决方案1】:

我像这样覆盖.save()

class Example(models.Model):
    title = models.SlugField()
    version = models.PositiveSmallIntegerField()
    content = models.TextField(blank=True)
    
    class Meta:
        unique_together = (
            ('title', 'version'),
        )
    
    def _highest_version(self):
        return Example.objects.filter(title=self.title).aggregate(Max('version'))['version__max'] or 0
    
    def save(self):
        if self.version is None:
            self.version = self._highest_version() + 1
        return super().save()

(根据您的验证故事进行必要的调整)

【讨论】:

  • 你的解决方案不是原子的
  • 确实不是!我不确定事务是否能保证这一点,如果您需要使用更高级的 SQL 功能,或者您是否必须破解 ORM 以允许 INSERT 中的表达式。就我而言,我只在低流量应用程序的特定流程点创建快照。我认为冲突的可能性很低,如果确实发生了,unique_together 将阻止它进入数据库。
  • 我曾建议使用transaction.atomic()(从django.db 导入)来解决此问题并使此解决方案具有原子性。我可以知道你为什么删除我的编辑Zoe 吗?谢谢
猜你喜欢
  • 2011-05-10
  • 2016-06-11
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 2021-09-24
  • 2011-10-23
  • 1970-01-01
  • 2019-08-19
相关资源
最近更新 更多