30

For our Django App, we'd like to get an AutoField to start at a number other than 1. There doesn't seem to be an obvious way to do this. Any ideas?

Fenix
  • 1,552
  • 2
  • 23
  • 44
user9845
  • 303
  • 1
  • 3
  • 5

9 Answers9

22

Like the others have said, this would be much easier to do on the database side than the Django side.

For Postgres, it'd be like so: ALTER SEQUENCE sequence_name RESTART WITH 12345; Look at your own DB engine's docs for how you'd do it there.

AdamKG
  • 13,678
  • 3
  • 38
  • 46
  • 3
    Doesn't this heavily couple your application to a particular DBMS though? – Adam Parkin Oct 02 '18 at 18:49
  • 2
    Hello mid-2019, any better answers? – vignz.pie May 29 '19 at 10:53
  • 1
    If your app is called cars and model is called Tesla then your command for changing next Mercedes id is: ```inPQAdmin: SELECT setval('cars_teslas_id_seq', 10000, true); ``` Next Tesla created in DB will have id = 10 000 – Hvitis Jan 26 '21 at 22:33
  • 1
    I'm sure in 2020 there are much better ways to do this. As Adam mentioned, this couples your dbms with your application, which sort of defeats the purposes of using something like django in the first place, imo. – logicOnAbstractions Feb 19 '21 at 16:28
13

For MySQL i created a signal that does this after syncdb:

from django.db.models.signals import post_syncdb
from project.app import models as app_models

def auto_increment_start(sender, **kwargs):
    from django.db import connection, transaction
    cursor = connection.cursor()
    cursor = cursor.execute("""
                                ALTER table app_table AUTO_INCREMENT=2000
                            """)
    transaction.commit_unless_managed()

post_syncdb.connect(auto_increment_start, sender=app_models)

After a syncdb the alter table statement is executed. This will exempt you from having to login into mysql and issuing it manually.

EDIT: I know this is an old thread, but I thought it might help someone.

Gabriel Samfira
  • 2,675
  • 1
  • 17
  • 19
  • 1
    I would recommend running the command directly in MySQL. One could put these in this command in the setup section of the README documents. – Prajwal May 24 '21 at 05:58
3

A quick peek at the source shows that there doesn't seem to be any option for this, probably because it doesn't always increment by one; it picks the next available key: "An IntegerField that automatically increments according to available IDs" — djangoproject.com

dfarrell07
  • 2,872
  • 2
  • 21
  • 26
William Keller
  • 5,256
  • 1
  • 25
  • 22
3

Here is what I did..

def update_auto_increment(value=5000, app_label="xxx_data"):
    """Update our increments"""
    from django.db import connection, transaction, router
    models = [m for m in get_models() if m._meta.app_label == app_label]
    cursor = connection.cursor()
    for model in models:
        _router = settings.DATABASES[router.db_for_write(model)]['NAME']
        alter_str = "ALTER table {}.{} AUTO_INCREMENT={}".format(
            _router, model._meta.db_table, value)
        cursor.execute(alter_str)
        transaction.commit_unless_managed()
rh0dium
  • 6,811
  • 4
  • 46
  • 79
2

I found a really easy solution to this! AutoField uses the previous value used to determine what the next value assigned will be. So I found that if I inserted a dummy value with the start AutoField value that I want, then following insertions will increment from that value.

A simple example in a few steps:

1.) models.py

class Product(models.Model):
    id = model.AutoField(primaryKey=True) # this is a dummy PK for now
    productID = models.IntegerField(default=0)
    productName = models.TextField()
    price = models.DecimalField(max_digits=6, decimal_places=2)
  • makemigrations
  • migrate

Once that is done, you will need to insert the initial row where "productID" holds a value of your desired AutoField start value. You can write a method or do it from django shell.

From view the insertion could look like this: views.py

from app.models import Product

dummy = {
   'productID': 100000,
   'productName': 'Item name',
   'price': 5.98,
}

Products.objects.create(**product)

Once inserted you can make the following change to your model:

models.py

class Product(models.Model):
    productID = models.AutoField(primary_key=True)
    productName = models.TextField()
    price = models.DecimalField(max_digits=6, decimal_places=2)

All following insertions will get a "productID" incrementing starting at 100000...100001...100002...

micshapicsha
  • 187
  • 3
  • 12
  • Dummy values does seem like the best solution to me. However, I'm not so sure about removing the `id` once set. It doesn't seem necessary to reverse it once it's in place. Additionally, this creates a situation where a re-deployment would require those additional steps - you wouldn't be able to just run the migrations again. – Joe Sadoski Aug 19 '21 at 20:53
1

The auto fields depend, to an extent, on the database driver being used.

You'll have to look at the objects actually created for the specific database to see what's happening.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
1

For those who are interested in a modern solution, I found out to be quite useful running the following handler in a post_migrate signal.

Inside your apps.py file:

import logging

from django.apps import AppConfig
from django.db import connection, transaction
from django.db.models.signals import post_migrate

logger = logging.getLogger(__name__)


def auto_increment_start(sender, **kwargs):
    min_value = 10000
    with connection.cursor() as cursor:
        logger.info('Altering BigAutoField starting value...')
        cursor.execute(f"""
        SELECT setval(pg_get_serial_sequence('"apiV1_workflowtemplate"','id'), coalesce(max("id"), {min_value}), max("id") IS NOT null) FROM "apiV1_workflowtemplate";
        SELECT setval(pg_get_serial_sequence('"apiV1_workflowtemplatecollection"','id'), coalesce(max("id"), {min_value}), max("id") IS NOT null) FROM "apiV1_workflowtemplatecollection";
        SELECT setval(pg_get_serial_sequence('"apiV1_workflowtemplatecategory"','id'), coalesce(max("id"), {min_value}), max("id") IS NOT null) FROM "apiV1_workflowtemplatecategory";
      """)
        transaction.atomic()
        logger.info(f'BigAutoField starting value changed successfully to {min_value}')


class Apiv1Config(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apiV1'

    def ready(self):
        post_migrate.connect(auto_increment_start, sender=self)

Of course the downside of this, as some already have pointed out, is that this is DB specific.

Barnercart
  • 1,523
  • 1
  • 11
  • 23
0

I needed to do something similar. I avoided the complex stuff and simply created two fields:

id_no = models.AutoField(unique=True)
my_highvalue_id = models.IntegerField(null=True)

In views.py, I then simply added a fixed number to the id_no:

my_highvalue_id = id_no + 1200

I'm not sure if it helps resolve your issue, but I think you may find it an easy go-around.

Sean
  • 586
  • 5
  • 8
0

In the model you can add this:

def save(self, *args, **kwargs):
       if not User.objects.count():
          self.id = 100
       else:
          self.id = User.objects.last().id + 1
       super(User, self).save(*args, **kwargs)

This works only if the DataBase is currently empty (no objects), so the first item will be assigned id 100 (if no previous objects exist) and next inserts will follow the last id + 1

Michael
  • 288
  • 1
  • 7