每次在Django中触摸对象时,将递增整数添加到queryset

时间:2019-02-17 22:28:00

标签: django django-queryset

我正在尝试添加一个递增整数,在这种情况下,此序列号应为[1,n],其中n是给定计划中的序列数。

由于序列会定期更新,我想知道循环浏览以下内容的最有效方法

Sequence.objects.filter(schedule=schedule).order_by('start', 'id')

,并为每个序列分配递增编号。另外,如果我希望每次创建,删除新序列或更新start字段时都运行该方法,那么在哪里是实现此方法的理想场所。我一直在考虑save()delete()方法,但是当序列中仅其他字段更新时,我不希望更新。

这是我的序列模型

class Sequence(models.Model):

    number = models.PositiveIntegerField(
        verbose_name='sequence number',
        help_text='auto incrementing sequence number',
    )
    schedule = models.ForeignKey(
        to=Schedule,
        verbose_name='schedule',
        on_delete=models.CASCADE,
        related_name='sequences',
        help_text='schedule primary key',
    )
    start = models.DateTimeField(
        verbose_name='start date',
        help_text='sequence starting datetime',
    )
    end = models.DateTimeField(
        verbose_name='end date',
        help_text='sequence ending datetime',
    )

1 个答案:

答案 0 :(得分:1)

好吧,您可以(不用说“应该”)为此使用Django signals

  

Django包括一个“信号分配器”,当动作在框架中的其他位置发生时,该信号分配器可帮助脱钩的应用程序得到通知。简而言之,信号允许某些发送者通知一组接收者已经采取了某些措施。 当许多代码片段可能对同一事件感兴趣时,它们特别有用。

然后您可以根据Sequence模型正在发送的信号(post_save,pre_save,pre_delete等)使用函数(接收器)进行更新。

为此,您不应覆盖savedelete。您的逻辑涉及对多个实例的更改,因此您应相对于单个实例保留保存和删除操作。

另一方面,您可以使用F表达式来有效地更新序列实例。

您会在文档中找到一个很好的例子。

https://docs.djangoproject.com/en/2.1/ref/models/expressions/#f-expressions

更新:

好吧,在最初的答案中,我指出了要走的路,现在我将向您展示整个过程,希望您能自己弄清楚。

我尝试使用信号,但得出的结论是这是错误的方法。信号将为每个save()触发。因此触发了无限循环。

这不是错误的方法,您使用了错误的信号/实现;)。

根据您的要求,我可以告诉您存在对应关系:

update sequence instance (save)    --->    do not change indices 
delete sequence instance           --->    update indices  
create sequence instance           --->    add incremented index

因此,我们有信号delete和信号post_save,它接收布尔值作为第三个参数,说明实例是已创建还是刚刚更新(您读了我共享的链接吗?和你在一起?)。

然后...

删除Sequence实例时更新索引:

@receiver(delete, sender=Sequence)
def deletion_handler(sender, instance, **kwargs):
    # Since number is incremental we only must update
    # those that are after the instance being deleted.
    number = instance.number
    sequences_for_update = Sequence.objects.filter(number__gt=number)
    # Decrement 1 to all sequences after number. No for loop.
    sequences_for_update.update(number=F('number') - 1)

在创建新实例时更新索引

@receiver(post_save, sender=Sequence)
def creation_handler(sender, instance, created, **kwargs):
    if created:
        # We only care about created instances (this avoid the infinite
        # loop you're talking about).
        # Now take the last number.
        instance.number = Sequence.objects.aggregate(last=Max('number'))['last'] + 1
        instance.save()