Django BigInteger自增字段作为主键?

38 投票
7 回答
32756 浏览
提问于 2025-04-15 21:48

我现在正在做一个项目,这个项目涉及到很多用户的共同智慧。每个访问网站的用户都会被创建一个独特的个人资料,他们的数据会被用来计算他们和其他用户之间的最佳匹配。

默认情况下,Django会创建一个INT(11)的id字段来处理模型的主键。我担心这个字段会很快溢出(也就是说,大约有24亿个设备访问这个页面而没有设置之前的cookie)。我该如何把它改成在MySQL中用BIGINT表示,并在Django中用long()表示呢?

我发现我可以这样做(http://docs.djangoproject.com/en/dev/ref/models/fields/#bigintegerfield):

class MyProfile(models.Model):
    id = BigIntegerField(primary_key=True)

但是有没有办法让它像普通的id字段那样自动递增呢?另外,我能不能把它设置为无符号,这样我就能有更多的空间来填充数据?

谢谢!

7 个回答

18

受lfagundes的启发,但我做了一个小而重要的修正:

class BigAutoField(fields.AutoField):
    def db_type(self, connection):  # pylint: disable=W0621
        if 'mysql' in connection.__class__.__module__:
            return 'bigint AUTO_INCREMENT'
        return super(BigAutoField, self).db_type(connection)

add_introspection_rules([], [r"^a\.b\.c\.BigAutoField"])

注意,我不是在扩展BigIntegerField,而是在扩展AutoField。这是一个重要的区别。使用AutoField时,Django会从数据库中获取自动递增的id,而BigInteger则不会。

从BigIntegerField切换到AutoField时,有一个担心是AutoField会把数据转换成int类型。

看看Django的AutoField:

def to_python(self, value):
    if value is None:
        return value
    try:
        return int(value)
    except (TypeError, ValueError):
        msg = self.error_messages['invalid'] % str(value)
        raise exceptions.ValidationError(msg)

还有

def get_prep_value(self, value):
    if value is None:
        return None
    return int(value)

结果证明这是可以的,在python命令行中验证过:

>>> l2 = 99999999999999999999999999999
>>> type(l2)
<type 'long'>
>>> int(l2)
99999999999999999999999999999L
>>> type(l2)
<type 'long'>
>>> type(int(l2))
<type 'long'>

换句话说,转换成int类型不会截断数字,也不会改变底层的数据类型。

21

如果你使用的是Django 1.10,现在它内置了一个叫做BigAutoField的功能:

https://docs.djangoproject.com/en/1.10/ref/models/fields/#bigautofield

7

从Django 3.2开始,你可以通过设置DEFAULT_AUTO_FIELD来控制隐式主键的类型了。这意味着你不再需要在每个模型里都手动覆盖主键。

#This setting will change all implicitly added primary keys to BigAutoField
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

值得注意的是,从Django 3.2开始,新创建的项目默认会把DEFAULT_AUTO_FIELD设置为BigAutoField

撰写回答