NIKITOS
NIKITOS

Reputation: 35

How to reset auto increment number every year in Django

I have a model IncomingCorrespondence with auto incrementing field ID. I also have field number, and I want two things for this field:

  1. This field will auto-increment its value, just like ID
  2. Every new year its value will start over from 0 (or 1)
ID Number Date
285 285 2020-03-12
286 286 2020-04-19
287 1 2021-01-01
class IncomingCorrespondence(models.Model):
   ID = models.models.AutoField(primary_key=True)
   date = models.DateField(null=True)
   number = models.IntegerField(null=True)

How can I do that the most efficient and reliable way?

Upvotes: 1

Views: 1110

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476803

You do not need to store the number, you can simply derive it by the number of items that are stored in the database since it has turned that year with:

class IncomingCorrespondence(models.Model):
    date = models.DateField(null=True)
    created = models.DateTimeField(auto_now_add=True)

    @property
    def number(self):
        return IncomingCorrespondence._base_manager.filter(
            created__year=self.created.year,
            created__lt=self.created
        ).count() + 1

We thus have a timestamp created that will store at what datetime the object was created, and we count the number of IncomingCorrespondence for that year before the timestamp plus one. You can in that case work with a package like django-softdelete [GitHub] to keep deleted objects in the database, and just filter these out when viewing the item.

another way might be to assign the maximum plus one to a field:

from django.db.models import Max
from django.utils.timezone import now

def next_number():
    data = IncomingCorrespondence._base_manager.filter(
        date__year=now().year
    ).aggregate(
        max_number=Max('number')
    )['max_number'] or 0
    return data + 1

class IncomingCorrespondence(models.Model):
   ID = models.models.AutoField(primary_key=True)
   date = models.DateField(auto_now_add=True)
   number = models.IntegerField(default=next_number, editable=False)

But here Django will dispatch numbers through a query. If there are multiple threads that concurrently create an IncomingCorrespondence, then this can fail. It also depends on the insertion time, not the date of the IncomingCorrespondence object.

Upvotes: 1

sudden_appearance
sudden_appearance

Reputation: 2197

You should count number with some method like querying count of created IncomingCorrespondence this year. It should not be done any other way (cronjob for example) as it won't be stable (crontab may fail and you will end up with anomalies (and won't even be able to notice that), or you will create instance right before crontab reset the sequence)

Upvotes: 0

Related Questions