[Fixed]-Django non primary_key AutoField


What I think could work is using an IntegerField (pretty much what an AutoField uses under the hood), and increment that on the model’s first save (before it’s ever put into the database).

I wrote an example model to show this below.

from django.db import models

class MyModel(models.Model):

    # This is what you would increment on save
    # Default this to one as a starting point
    display_id = models.IntegerField(default=1)

    # Rest of your model data

    def save(self, *args, **kwargs):
        # This means that the model isn't saved to the database yet
        if self._state.adding:
            # Get the maximum display_id value from the database
            last_id = self.objects.all().aggregate(largest=models.Max('display_id'))['largest']

            # aggregate can return None! Check it first.
            # If it isn't none, just use the last ID specified (which should be the greatest) and add one to it
            if last_id is not None:
                self.display_id = last_id + 1

        super(MyModel, self).save(*args, **kwargs)

This, in theory, just replicates what AutoField does, just with a different model field.


Assuming there is no sequence support in the chosen DBMS, a solution is to create a model:

class Counter(models.Model):
    count = models.PositiveIntegerField(default=0)

    def get_next(cls):
        with transaction.atomic():
            cls.objects.update(count=models.F('count') + 1)
            return cls.objects.values_list('count', flat=True)[0]

and create one instance of it in a data migration. This could have some implications if you’re using transaction management, but it’s (if your DBMS supports transactions) guaranteed to always return the next number, regardless of how many objects have been there at the start of a transaction and whether any had been deleted.


You can also using count as auto increment. In my project I’m using like this.

def ids():
    no = Employee.objects.count()
    if no == None:
        return 1
        return no + 1
emp_id = models.IntegerField(('Code'), default=ids, unique=True, editable=False)

Leave a comment