【问题标题】:How should i implement custom counter of instances with some (same) type?我应该如何实现具有某些(相同)类型的实例的自定义计数器?
【发布时间】:2018-05-25 21:00:54
【问题描述】:

这是我的模型:

class MyModel(BaseModel):
    no = models.PositiveIntegerField()  # Human-friendly no -> counter that starts from 1 for every type
    type = models.ForeignKey(MyModelType)

我在模型的pre_save 信号中设置了no 字段的值:

@receiver(pre_save, sender=MyModel)
def mymodel_pre_save(sender, instance, *args, **kwargs):
    if not instance._state.adding:
        return

    obj_count = MyModel.objects.filter(type=instance.type).count()
    instance.no = obj_count + 1 

此代码的错误在于它生成具有相同no 编号的不同对象。当多个用户同时创建对象时,可能会发生这种情况。

修复它的最佳方法是什么?在高负载环境下,将模型的.save() 方法移动分配就足够了吗?

解决方案

最终,我实现了@moooeeeep 的解决方案,但将逻辑从post_save 移至模型的save 方法:

class MyModel(BaseModel):
    ...

    def save(self, *args, **kwargs):
        adding = self.pk is None

        with transaction.atomic():
            super().save(*args, **kwargs)

            if adding:
                # Compute and set `.no`
                obj_count = MyModel.objects.filter(type=self.type, id__lte=self.id).count()
                self.no = obj_count
                self.save(update_fields=['no'])

【问题讨论】:

  • 您是否正在尝试做这样的事情? stackoverflow.com/q/21128899/1025391
  • @moooeeeep,不。一个简单的AutoField 字段是行不通的。我需要计数器来计算具有相同 type 值的记录。

标签: django


【解决方案1】:

我可以想象您的问题是由竞争条件引起的:

  • 一个请求要求计数,
  • 另一个请求要求计数,
  • 一个请求插入一个 count+1 的新项目,
  • 另一个请求插入一个具有相同计数+1 的新项目。

我能想象的最直接的方法是,您不要在插入之前设置项目的计数,而是在之后设置。然后,您通过计算具有适当类型的项目数来计算该类型对象的计数,其中id 小于或等于相应对象的id。然后在 post_save() 处理程序中更新该值。

请注意,当您在计数后删除项目时,您的计数字段可能仍会出错。当您希望允许删除项目时,您可能需要使用大于您要删除的 idid 更新所有计数。

总之,完全从 Django 方面分配计数可能是个坏主意。考虑使用数据库功能将计数分配为从(广义上的)max(count) 计算的默认值,其中类型等于新插入项目的类型或零加一。并且不要担心将被删除的项目。如果您想要准确计数,请在需要时进行计数查询。

【讨论】:

    【解决方案2】:

    您可以使用 select_for_update() 方法获取 db 牵引探针的实例对象。但是在您的高负载环境中,select_for_update 方法会导致等待。

    解决方案1:建议你把这个count change code从pre_save移到post_save,如果是这样,你不需要加一,把新的count放到no字段就行了。

    解决方案2:不要将类型计数存储在数据库中,只需从查询中获取或缓存在redisDB中即可。

    对不起,我的英语很差。

    【讨论】:

    • 1.我担心如果post_save 信号中发生了一些不好的事情,那么no 字段中会有空值的实例。 2.我认为,当旧实例由于从数据库中删除一些其他实例等而获得更新的计数器值时,动态计算计数器可能会导致不一致的情况。
    • 你不应该使用post_save,如果count的值不能非常一致,你应该使用后台离线任务在db或cachedb中更新它。你的域上下文需要更清楚。
    猜你喜欢
    • 2012-12-06
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    • 2016-02-09
    • 2014-10-24
    • 2020-03-21
    • 2016-07-04
    • 1970-01-01
    相关资源
    最近更新 更多