Generating a unique 10 digit ID for each model instance

1,867 views
Skip to first unread message

Jack Zhang

unread,
Oct 22, 2017, 11:52:43 AM10/22/17
to Django users
Let's say I have a model called 'Dogs'.  Users can create instances of Dogs.  For every Dogs instance that is created, I want to assign a globally unique ID to it.  The ID will be 3 capitalized letters followed by 7 numbers.  E.g. ABC1234567, POZ2930193

What is the easiest way to go about doing this?  I looked into UUID but it generates a longer string.  Keep in mind that the ID must be globally unique - so after one has been created, it should not be created again.

Thanks.

Andréas Kühne

unread,
Oct 22, 2017, 12:30:02 PM10/22/17
to django...@googlegroups.com
Hi,

When you say "globally unique" - I am supposing you mean within your application?

What you need to do is set a field to be the primary key, see : https://docs.djangoproject.com/en/1.11/topics/db/models/#automatic-primary-key-fields

However - it would be simpler to use the standard primary key auto id field, and then add another field to hold your unique ID in it. This could then be created on the pre_save signal and you could write something that randomly generates the unique ID field. See https://docs.djangoproject.com/en/1.11/topics/signals/

Best regards,

Andréas

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/0cd8e33c-8c92-471c-9c36-ac82749750b4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Avraham Serour

unread,
Oct 22, 2017, 1:31:23 PM10/22/17
to django-users
The database usually handles this and you don't need to worry, there are many corner cases and DB systems are able to handle them

But they won't look like whatever format you would like, usually it is just a number

Why do you need the IDs to look like that?

--

Jack Zhang

unread,
Oct 22, 2017, 2:53:52 PM10/22/17
to Django users
Hi Andréas,

Yes, by globally unique I mean a unique 10-digit ID is assigned to every model instance of 'Dogs' within my application.  This ID will be shown publicly on the website to allow easy search access to a specific model instance.

I think you are right about creating a new field to hold the unique ID, instead of using the primary key.  Generating the ID is not too difficult, but how can I ensure that I won't generate a duplicate ID?  From briefly thinking about it, this is what I got...

When a new instance is created, generate new ID
For every existing ID in the database:
     If new ID == existing ID in the database:
          Generate new ID and re-run the for loop
If new ID does not match any existing ID  in the database, proceed with continuing the model instance.

Also I read up on Signals, but is it necessary?


On Sunday, October 22, 2017 at 12:30:02 PM UTC-4, Andréas Kühne wrote:
Hi,

When you say "globally unique" - I am supposing you mean within your application?

What you need to do is set a field to be the primary key, see : https://docs.djangoproject.com/en/1.11/topics/db/models/#automatic-primary-key-fields

However - it would be simpler to use the standard primary key auto id field, and then add another field to hold your unique ID in it. This could then be created on the pre_save signal and you could write something that randomly generates the unique ID field. See https://docs.djangoproject.com/en/1.11/topics/signals/

Best regards,

Andréas
2017-10-22 17:52 GMT+02:00 Jack Zhang <valac...@gmail.com>:
Let's say I have a model called 'Dogs'.  Users can create instances of Dogs.  For every Dogs instance that is created, I want to assign a globally unique ID to it.  The ID will be 3 capitalized letters followed by 7 numbers.  E.g. ABC1234567, POZ2930193

What is the easiest way to go about doing this?  I looked into UUID but it generates a longer string.  Keep in mind that the ID must be globally unique - so after one has been created, it should not be created again.

Thanks.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Jack Zhang

unread,
Oct 22, 2017, 2:55:51 PM10/22/17
to Django users
I need this 10-digit unique ID for every model instance so users can easily search up a specific model instance.  It will be publicly displayed on the website, unlike a primary key which is usually hidden


On Sunday, October 22, 2017 at 1:31:23 PM UTC-4, Avraham Serour wrote:
The database usually handles this and you don't need to worry, there are many corner cases and DB systems are able to handle them

But they won't look like whatever format you would like, usually it is just a number

Why do you need the IDs to look like that?
On Oct 22, 2017 6:53 PM, "Jack Zhang" <valac...@gmail.com> wrote:
Let's say I have a model called 'Dogs'.  Users can create instances of Dogs.  For every Dogs instance that is created, I want to assign a globally unique ID to it.  The ID will be 3 capitalized letters followed by 7 numbers.  E.g. ABC1234567, POZ2930193

What is the easiest way to go about doing this?  I looked into UUID but it generates a longer string.  Keep in mind that the ID must be globally unique - so after one has been created, it should not be created again.

Thanks.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Andréas Kühne

unread,
Oct 22, 2017, 3:30:19 PM10/22/17
to django...@googlegroups.com
Hi,

I think you are correct with your pseudocode - you can do a Model.objects.filter(unique_code==random_code).count() - and then loop on that. It should work.

The reason why I thought you should use the signals is because thats how I have done the same things in the past. However, you can just overload the save method and then do it there (if there is no PK).

Regards,

Andréas

To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

James Schneider

unread,
Oct 22, 2017, 6:58:46 PM10/22/17
to django...@googlegroups.com


On Oct 22, 2017 12:29 PM, "Andréas Kühne" <andrea...@hypercode.se> wrote:
Hi,

I think you are correct with your pseudocode - you can do a Model.objects.filter(unique_code==random_code).count() - and then loop on that. It should work.

I wouldn't do this, it can lead to a race condition where you can possibly end up with duplicate ID's if two objects are being inserted at the same time.

Ensure that you have a unique index on the ID column, then just create your ID and try to insert it into the DB. Let the DB determine whether or not it is unique, it is much better at this than any Python code you can write. If it falils, catch the insertion error, change the ID, and try again. 

Even if you did do this, you should use .exists() rather than .count().

Either way, your code should be prepared to deal with the potential for duplicate ID's being inserted in to the database.

-James

Mark Phillips

unread,
Oct 22, 2017, 7:37:42 PM10/22/17
to django users
I thought python's uuid.uuid4 guaranteed a unique value. Am I missing something?

Mark

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

James Schneider

unread,
Oct 23, 2017, 4:32:28 AM10/23/17
to django...@googlegroups.com


On Oct 22, 2017 8:52 AM, "Jack Zhang" <valac...@gmail.com> wrote:
Let's say I have a model called 'Dogs'.  Users can create instances of Dogs.  For every Dogs instance that is created, I want to assign a globally unique ID to it.  The ID will be 3 capitalized letters followed by 7 numbers.  E.g. ABC1234567, POZ2930193

What is the easiest way to go about doing this?  I looked into UUID but it generates a longer string.  Keep in mind that the ID must be globally unique - so after one has been created, it should not be created again.

A unique index constraint on the field will ensure that duplicate values are not allowed. There are several ways to go about doing this. Will the ID's be sequential or random? 

If sequential, all of your save operations where an ID is created should be within transaction blocks. ORM operations like .last() come in handy to determine the next available ID. 

One option to consider is splitting the ID into two model fields, one for the 3 character prefix, and the second for the numerical suffix. The "ID" would then be presented to the end user as the concatenation of the two fields. Doing this provides two advantages. Searching against a text string is slow compared to a numerical search. An ID provided by the user is then searched within the DB using the two fields after being easily split in Python, taking advantage of native type indexing in the DB. Secondly, you can choose to take advantage of the DB's auto-increment functionality on the second field. The DB is way more efficient for tracking that. Sure, it will start with 1 and work its way up, but to make a 7 digit number out of that, you just pad it with six zeros before combining it with the prefix just before you display it to the user. It wouldn't matter what the prefix value is at that point. All you would need is the 7 digit code (which would contain mostly zeros at first) to find any record. The prefix would simply be for human interpretation. This strategy does have the side affect of two prefixes never having the same suffix, meaning you'll never have an ID value of ABC0000001 and POZ0000001 at the same time. This does limit you to a total of 9,999,999 items between all of your prefixes though. 

If it will be random, then you simply have a math.random(1,10000000) function, but you'll need extra logic to determine uniqueness and handle collisions. I'd still recommend breaking it up in to two fields if you know that is always going to be the format of your ID. Keep in mind that if you go the random route, you can never delete a record once it has been created to ensure that the ID is not reused. In this case, the .exists() method is your friend, or you can use the insertion strategy I mentioned in my other thread.

-James

James Schneider

unread,
Oct 23, 2017, 4:38:04 AM10/23/17
to django...@googlegroups.com


On Oct 22, 2017 4:36 PM, "Mark Phillips" <ma...@phillipsmarketing.biz> wrote:
I thought python's uuid.uuid4 guaranteed a unique value. Am I missing something?

Mark

From a practical perspective, you are correct, uuid.uuid4() will create a unique value every time. There is no guarantee, though, as eventually you'll run out of values with enough iterations. You probably have a better chance of being struck by lightning 6 times in a row, though, before you get a UUID4 collision.

However, the OP is generating a 7 digit integer, meaning that UUID4 is out.

-James

James Schneider

unread,
Oct 23, 2017, 4:51:45 AM10/23/17
to django...@googlegroups.com


On Oct 22, 2017 9:29 AM, "Andréas Kühne" <andrea...@hypercode.se> wrote:
Hi,

When you say "globally unique" - I am supposing you mean within your application?

What you need to do is set a field to be the primary key, see : https://docs.djangoproject.com/en/1.11/topics/db/models/#automatic-primary-key-fields

Not necessarily. The field only need be unique, but does not need to be the primary key, and probably shouldn't be IMO. Data (in this case, an arbitrary ID) should not be used for the internal linkage of the DB. That's what PK's are for. What if the ID needs to be changed later? All of the relations would also need to be updated, leading to a large headache at best.


However - it would be simpler to use the standard primary key auto id field, and then add another field to hold your unique ID in it. This could then be created on the pre_save signal and you could write something that randomly generates the unique ID field. See https://docs.djangoproject.com/en/1.11/topics/signals/

Again, I have to disagree. This shouldn't be a pre_save signal. What happens when a model save() fails due to a race condition where another object grabs the ID that was generated in the pre_save signal? The save() function is likely the best place, or maybe a post_save function that generates the ID and updates the existing record with a unique ID value. That way the model can save its data and be 'assigned' an ID as the next operation. I would personally determine and assign everything in the model save() function, though, and make it one operation with logic to handle collisions.

-James

Andréas Kühne

unread,
Oct 23, 2017, 5:33:51 AM10/23/17
to django...@googlegroups.com
James,

I agree with you - after thinking about it for another day, your solution would be the best.

Regards,

Andréas

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Jack Zhang

unread,
Oct 23, 2017, 9:36:36 PM10/23/17
to Django users
Hi James,

First of all thank you for all your detailed replies.  You have a wealth of knowledge.  

I took your advice and chose the separated method.  I decided to use 2 letters + 8 numbers because 3 letters can render results like 'SEX' and 'FUK'.

I think I got it right.  Here is my code:

class Dogs(models.Model):

    def random_number():
        a = random.randint(10000000, 99999999)
        b = Model.objects.values_list('id_numbers')
        for num in b:
            if a == b:
                a = random.randint(10000000, 99999999)
        return a
    
    def random_letter():
        import string
        a = ''
        for i in range(2):
            a += random.choice(string.ascii_uppercase)
        return a
    
    id_numbers = models.IntegerField(default = random_number)
    id_letters = models.CharField(max_length=2, default = random_letter)
Reply all
Reply to author
Forward
0 new messages