Location>code7788 >text

How to elegantly generate non-repeating IDs in Django projects?

Popularity:746 ℃/2024-12-10 15:12:26

preamble

Originally, the title was "Generate non-repeating 4-digit numbers", but the simple numbers are a bit limited, so I'll promote it to become a non-repeating ID.

This feature was encountered while working on this small project in the image below, kind of like WeChat's face-to-face group building, where a random and non-repeating password is generated and other people can join the classroom by typing in this password.

LearnMates

There are quite a few ways to accomplish this, which are briefly documented in this article.

No dependency on third-party libraries

The first step is to implement this functionality purely based on Django ORM

First define a model

from  import models

class MyModel():
    unique_code = (max_length=4, unique=True)

single-task

The easiest and most brutal way to do this is to write a dead end loop.

import random

def generate_unique_code():
    while True:
        code = str((1000, 9999))
        if not (unique_code=code).exists():
            return code

This implementation is certainly fine in a single-threaded test environment, though the operation is not atomic and concurrent environments may generate duplicate numbers.

Consider concurrency

Database transactions or optimistic locking can be used in highly concurrent situations.

from import transaction, IntegrityError

def create_instance():
	  # Number of attempts
  	retry = 10
    for _ in range(retry):
        code = generate_unique_code()
        try:
            with ():
                instance = MyModel(unique_code=code)
                ()
            return instance
        except IntegrityError:
            # If a uniqueness conflict arises,retry
            continue
    raise Exception("Unable to generate a unique four-digit number")

pre-generated

Both of the previous methods require frequent database reads and have poorer performance.

You can also use space for time, because it's only four digits, 0000-9999 This range of numbers is not much, pre-stored the 10,000 rows into the database, add aavailable field

When it is necessary to generate a unique ID, first filter theavailable == True of the data, and then randomly select one; and set this field to theFalse

That's the general idea.

Using third-party libraries

In this project, I paired these three libraries (which is the main purpose of this post, to document these libraries)

  • shortuuid
  • hashids
  • django-autoslug

shortuuid

shortuuid is a lightweight library that generates relatively short UUIDs

It's easy to do this with this library

import shortuuid
(alphabet="0123456789").random(length=4)

But for this project, I didn't use the library for this

In fact, the library has a uuid as its name implies, and is naturally used to do work with python's built-in UUIDs

I used this library to convert theClient Model ID simplified to 7 digits

class Client(ModelExt):
    client_id = (default=uuid.uuid4, editable=False)
    client_key = (max_length=100, default=uuid.uuid4)
    user = (
        User, on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True, unique=True,
    )
    consumer_name = (max_length=100, null=True, blank=True)
    is_online = (default=False)

    def short_client_id(self):
        short = (self.client_id)
        return short[:7]

utilization method can turn a 32-bit UUID into a 22-bit one, and can also be used to change a 32-bit UUID into a 22-bit one using thedecode Methodological recovery

hashids

This is a library that converts numbers to short strings

Despite the hash in its name, the library's encoding is reversible

import hashids
h = ()
(123, 456)
# Out[15]: 'X68fkp'
('X68fkp')
# Out[16]: (123, 456)

I use it to generate classroom names based on timestamps

def get_timestamp_hashid():
    hashids = Hashids(salt='hahaha salt lala')
    t = ().timestamp()
    result = tuple(map(int, str(t).split('.')))
    return (*result)

on account ofencode method receives a number (which can also be a tuple containing a number), so here the integer and decimal parts of the timestamp are converted to tuples and passed into hashids

django-autoslug

This library is used to generate field-based unique slugs with customizable generation logic.

That's the library I use for the generate unique classroom password function (but not really recommending this approach)

from autoslug import AutoSlugField


def populate_classroom_number(instance):
    return str((1000, 9999))
  
class ClassroomIdiom(ModelExt):
    name = (max_length=100)
    number = AutoSlugField(populate_from=populate_classroom_number, unique=True)

The principle of this library is very simple, according to user-defined rules to generate slug, and then check the database for duplicates, encounter duplicates, then append the number, which may result in the generation of more than 4 digits.

The best is still what I said earlierNo dependency on third-party libraries of the third way.

The use of django-autoslug here is simply a lazy way to leave the complex judgment logic to a third-party library - it's just a toy project, after all.

wrap-up

When it comes to generating unique IDs, Django is no different than any other backend framework. The idea is similar, except that you can get lazy with the Python ecosystem...