返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
Django

How to elegantly generate unique IDs in a Django project?

When it comes to generating unique IDs, Django isn't much different from other backend frameworks—the thinking is similar, except you can cut corners a bit by leveraging the Python ecosystem…

曦远 (DealiAxy)约 16 分钟
目录12 个章节

Introduction

Originally, the title was intended to be "Generating Non-Repetitive Four-Digit Numbers," but since numbers alone are somewhat limiting, it's been generalized to non-repetitive IDs.

This feature was encountered while working on the small project shown in the image below, which is somewhat similar to WeChat's "Face-to-Face Group Creation." It generates a random and unique password, and others can join the classroom by entering this password.

LearnMates

There are several ways to implement this feature, and this article will briefly document one approach.

Without Relying on Third-Party Libraries

First, let's implement this function purely based on Django ORM.

First, define a model.

from django.db import models

class MyModel(models.Model):
    unique_code = models.CharField(max_length=4, unique=True)

Single Task

The simplest and most straightforward method is to write an infinite loop.

import random

def generate_unique_code():
    while True:
        code = str(random.randint(1000, 9999))
        if not MyModel.objects.filter(unique_code=code).exists():
            return code

This implementation is definitely reliable in a single-threaded testing environment. However, this operation is not atomic, and in a concurrent environment, it may generate duplicate numbers.

Considering Concurrency

In high-concurrency scenarios, you can use database transactions or optimistic locking.

from django.db import transaction, IntegrityError

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

Pre-generating

The first two methods require frequent database reads, resulting in relatively poor performance.

Alternatively, we can trade space for time. Since it's only four-digit numbers, the range from 0000 to 9999 doesn't contain too many values. We can pre-save these ten thousand rows into the database and add an available field.

When we need to generate a unique ID, we first filter for data where available == True, then randomly select one; and set this field to False.

This is the general idea.

Using Third-Party Libraries

In this project, I used these three libraries together (this is also the main purpose of writing this article, to document these libraries)

  • shortuuid
  • hashids
  • django-autoslug

shortuuid

shortuuid is a lightweight library for generating shorter UUIDs.

Implementing this functionality using this library is straightforward.

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

However, in this project, I did not use this library for this purpose.

In fact, as the name suggests, this library has "uuid" in it and is naturally used for tasks related to Python's built-in UUID.

I used this library to simplify the ID of the Client model to 7 characters.

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

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

The shortuuid.encode method can convert a 32-character UUID into 22 characters, and it can be restored using the decode method.

hashids

This is a library for converting numbers into short strings

Although the name includes "hash," the encoding of this library is reversible

import hashids
h = hashids.Hashids()
h.encode(123, 456)

Out[15]: 'X68fkp'

h.decode('X68fkp')

Out[16]: (123, 456)


I used it to generate classroom names based on timestamps

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

Because the encode method accepts numbers (or a tuple containing numbers), here the integer and decimal parts of the timestamp are converted into a tuple and then passed into hashids

django-autoslug

This library is used to generate unique slugs based on fields, and it also allows for custom generation logic.

I used this library to implement the unique classroom password generation feature (but I wouldn't particularly recommend this approach).

from autoslug import AutoSlugField


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

The principle behind this library is straightforward. It generates a slug based on user-defined rules, then checks the database for duplicates. If a duplicate is found, it appends a number to the end. This process can potentially result in numbers that exceed four digits.

The best approach is still the third method I mentioned earlier, which does not rely on any third-party libraries.

Using django-autoslug here was simply for convenience, delegating the complex logic to a third-party library. After all, this is just a toy project.

Summary

When it comes to generating unique IDs, Django isn't really different from other backend frameworks—the approach is quite similar. It's just that you can take advantage of the Python ecosystem to cut some corners...

_EOF_

How to elegantly generate unique IDs in a Django project?

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

给Django的Admin添加自定义Action-并移除需要选择对象的限制

前言 不得不说,Django的Admin真的给开发带来很多便利,这不,我又来折腾了,这次是添加自定义的action 这个自定义的Action可以看看官方文档的介绍,很详细,不再赘述。 https://docs.djangoproject.com/en/3.0/ref/contrib/admin/actions/ 界面效果 Django默认的添加完action是要选择下面的对象才能提交的,不过我不需

2022-08-26 · Django · 约 1 分钟

Django全文检索实现:HayStack-+-Whoosh-+-Jieba

涉及到的工具 haystack是django的开源搜索框架,该框架支持**Solr**, Elasticsearch, Whoosh, **Xapian搜索引擎,不用更改代码,直接切换引擎,减少代码量。 搜索引擎使用Whoosh,这是一个由纯Python实现的全文搜索引擎,没有二进制文件等,比较小巧,配置比较简单,当然性能自然略低。 中文分词Jieba,由于Whoosh自带的是英文分词,对中文的分

2022-08-26 · Django · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…