7

I saw this answer but there is no specific answer yet. I want to create custom id that starts with letter. When a new record comes into database I want to change the id to A00001, .... A00002, .... A00010, ...A10000 etc. The id will be always in range 99999- 00001 so how can I do that?

my model is simple:

class Custom(models.Model):
    id = models.AutoField(primary_key=True, editable=False)
  • What would you use this for? If you want to make an `id` with auto increment integer, you need a DBMS such as MySQL or a number counter using In-Memory DB like Redis. – sinwoobang Aug 29 '18 at 06:07
  • For production app. They wanted this `id` format –  Aug 29 '18 at 06:09
  • 1
    @Telmunn How about using id as just Integer and make an another field or property which add 'A00~' format string to id? – sinwoobang Aug 29 '18 at 06:11

5 Answers5

10

The AutoField field is a kind of IntegerField field, so you can't use PKs as A00001 .

So, the possible way to achieve the requirement is to change the AutoField to CharField.

Technically you can use "String PK Field" But, you should be aware of the problems/performance issues if you are going to use that.

Here I found one nice SO post that explains the same - Strings as Primary Keys in SQL Database

========================================================================

If you still really wish to migrate to String PKs, read the following

First you need to use the CharField instead of AutoField and override the save() method of model

from django.db.models import Max


class Custom(models.Model):
    id = models.CharField(primary_key=True, editable=False, max_length=10)
    name = models.CharField(max_length=100)

    def save(self, **kwargs):
        if not self.id:
            max = Custom.objects.aggregate(id_max=Max('id'))['id_max']
            self.id = "{}{:05d}".format('A', max if max is not None else 1)
        super().save(*kwargs)
JPG
  • 82,442
  • 19
  • 127
  • 206
  • @BearBrown sorry, I dont get you. Can you edit directly? – JPG Aug 29 '18 at 06:22
  • 8
    This code would be hard to use in production. If multiple users try it simultaneously, IntegrityError would be raised. – sinwoobang Aug 29 '18 at 06:23
  • @JPG you did a disservice, it can give many trouble with perfomance IMHO – Brown Bear Aug 29 '18 at 06:34
  • @BearBrown Yeah. btw, I updated the answer with little bit more information – JPG Aug 29 '18 at 07:04
  • @JPG I am getting ValueError: Unknown format code 'd' for object of type 'str' for the line self.id = "{}{:05d}".format('A', max if max is not None else 1) My ID if it is string then it should throw that error right? How will it work? – Sukanya Pai Jan 30 '20 at 12:36
  • 1
    @SukanyaPai The value of **`max`** expects in ***`int`*** type and in your case, it is **`str`**. – JPG Jan 31 '20 at 03:42
  • 1
    I had to modify this Line of assigning to self.id as below: self.id = "{}{:05d}".format('A', int(max[1:]) if max is not None else 1) Observer the slicing done and conversion to int in max variable. Now to solve the problem of getting the correct id_max, I followed the this: custom=Custom.objects.last() (new line) max=custom.id if custom is not None else None – Sukanya Pai Feb 03 '20 at 10:14
  • Hi @JPG, still getting Unknown format code 'd' for object of type 'str' for the line – Denis Jan 17 '23 at 10:10
5

string as Primary Key not good idea if you plan to do references to the table, so i recommend you to add a property, for example:

class Custom(models.Model):
    id = models.AutoField(primary_key=True, editable=False)

    @property
    def sid(self):
        return "A%05d" % self.id

and to do queries you can do processing the input values, for example:

s_input = "A%05d" % 231 # 'A00231'
number = s_input[1:] # '00231'
input_id = int(number) # 231
Brown Bear
  • 19,655
  • 10
  • 58
  • 76
  • I think Indent should be modified. – sinwoobang Aug 29 '18 at 06:17
  • glad to help you – Brown Bear Aug 29 '18 at 06:41
  • @BearBrown I tried your approach in my Django Model, but still my id field gets default 1,2,3 values while creating. Can you please help? I did the same thing, applied migrations and then started server and created data. – Sukanya Pai Jan 30 '20 at 12:26
  • @SukanyaPai in this solution the PK in the database is still integer and only property `sid` is string – Brown Bear Jan 31 '20 at 16:47
  • @Bear Brown does it mean the sid property is the new ID that I should track? I checked my database, there was no sid in my table. I have performed makemigrations and migrate operations as well. Can you tell me where I went wrong please? – Sukanya Pai Feb 01 '20 at 12:55
  • sid is property of the model and it doesn't exists in the database – Brown Bear Feb 01 '20 at 13:02
  • @Bear Brown ok, so to clarify again the ID will be stored in the customised ID format right? Because for me it is storing as integers itself (1, 2, ...). I have applied migrations as well. Can you please help me where I am going wrong? – Sukanya Pai Feb 03 '20 at 00:51
4

I also have another way, That i use in my django project. Here are some code

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


id = models.CharField(primary_key=True, editable=False, max_length=30)
def save(self, **kwargs):
    if not self.id:
        self.id = "{}{:08d}".format('ABC', self.emp_id)
    super().save(*kwargs)
1

It's better to create a new field for the custom id in the models and the process in the backend. You can set that as primary_key with unique=True and editable=False:

class Custom(models.Model):
  id = models.Autofield(primary_key=True, editable=False, max_length=10)
  uid= models.CharField(max_length=100, unique=True)

  def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    self.set_uid()                                 # calling the set_uid function

  def set_uid(self):
    if not self.uid:                               # if uid of the instance is blank
      uid = "CUS" + str(self.id + (10 ** 5))       # generating the uid
      customer= Custom.objects.get(id=self.id)     # getting the instance
      customer.uid = uid                           # allocating the value
      customer.save()                              # saving the instance

  def __str__(self):
    return self.uid

Can also merge the set_uid() inside the save() where the function is called:

class Custom(models.Model):
  id = models.Autofield(primary_key=True, editable=False, max_length=10)
  uid= models.CharField(max_length=100, unique=True)

  def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    if not self.uid:                                # if uid of the instance is blank
      self.uid = "CUS" + str(self.id + (10 ** 5))   # generating the uid and allocating the value
      self.save()                                   # saving the instance
  
  def __str__(self):
    return self.uid
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
0

I tried to use answer of @JPG, but it has a bug. The bug is becasue it can't auto increment. I fixed the bug, and this my resultant code:

def save(self, **kwargs):
    if not self.id:
        max = YourModel.objects.aggregate(
            id_max=models.Max('id'))['id_max']
        if max is not None:
            max += 1
        else:
            max = 100
        self.id = "{:08d}".format(
            max)  # id from 100 to start
    super().save(*kwargs)
Yua Xan
  • 1
  • 2