我可以有一个未签名的自动字段吗?

问题描述 投票:0回答:3

我希望我的模型的主键未签名。因此我做了这样的事情:

class MyModel(models.Model):
    id = models.PositiveIntegerField(primary_key=True)

这会在生成的 MySQL 表中得到一个

UNSIGNED
列,这是我想要的。但是,我相信每次创建新对象时我都不会自动分配给
id
,不是吗?这似乎需要使用
AutoField
来代替。问题是,
AutoField
已签名。有没有办法创建一个无符号的
AutoField

django django-models
3个回答
5
投票

字段的实际类型是在后端指定的。对于 MySQL,后端是

django.db.backends.mysql
django/db/backends/mysql/creation.py
的摘录显示了以下翻译:

class DatabaseCreation(BaseDatabaseCreation):
    # This dictionary maps Field objects to their associated MySQL column
    # types, as strings. Column-type strings can contain format strings; they'll
    # be interpolated against the values of Field.__dict__ before being output.
    # If a column type is set to None, it won't be included in the output.
    data_types = {
        'AutoField':         'integer AUTO_INCREMENT',
        'BooleanField':      'bool',
        'CharField':         'varchar(%(max_length)s)',
        ...

要改变这一点,你应该对这个字典进行猴子补丁:

from django.db.backends.mysql.creation import DatabaseCreation
DatabaseCreation.data_types['AutoField'] = 'integer UNSIGNED AUTO_INCREMENT'

或者你创建自己的类,这样你就不会弄乱其他的

AutoFields

from django.db.models.fields import AutoField
class UnsignedAutoField(AutoField):
    def get_internal_type(self):
        return 'UnsignedAutoField'

from django.db.backends.mysql.creation import DatabaseCreation
DatabaseCreation.data_types['UnsignedAutoField'] = 'integer UNSIGNED AUTO_INCREMENT'

然后创建你自己的PK:

id = UnsignedAutoField()

当它从

AutoField
下降时,它将继承其所有行为。


0
投票

编辑:需要明确的是,我自己或 Simanas 编写的解决方案都不应该在现实世界的项目中使用。我写这篇文章是为了作为一个例子,如果他们决定避免使用 DBMS 内置方式,那么应该朝哪个方向发展,而不是作为一个可供使用的完整模型。

我很抱歉写了一个答案而不是对 Simanas 发表的帖子发表评论,但我没有很高的声誉来发布这个帖子,我觉得这是必要的,因为这个问题在“django autofield unsigned integer”关键字上排名相当高.

使用他的方法并不可靠,因为如果删除先前的对象之一,它将为新行生成一个现有整数。这是修改后的:

from django.db import IntegrityError
import re

class MyModel(models.Model):

    def next_id():
        try:
            # Find the ID of the last object
            last_row = MyModel.objects.order_by('-id')[0]
            return last_row.id + 1
        except IndexError:
            # No objects exist in database so far
            return 1

    id = models.PositiveIntegerField(primary_key=True, default=next_id)

    def save(self, *args, **kwargs):
        while True:
            try:
                super(MyModel, self).save(*args, **kwargs)
                break
            except IntegrityError, e:
                if e.args[0] == 1062:
                    if re.match("^Duplicate entry \'.*\' for key \'%s\'$"
                            % re.escape(self._meta.pk.name), e.args[1]):
                        self.id = next_id()
                    else:
                        raise

虽然这可行,但它不知道新分配的 ID 是否以前用于另一个对象(在删除最新对象的情况下?),并且在这种情况下可能会导致冲突;但与 Augusto 的答案相比,它可以跨数据库工作,这是 MySQL 特定的。

此方法的另一个警告是,如果您有另一个应用程序连接到同一数据库,则它必须在 INSERT 上提供 ID,因为自动增量不是在数据库级别完成的。

你几乎肯定不想这样做。


0
投票

也许我的回答对某人有用。在 Django v.5 中,它是这样完成的......

简单需要创建一个类:

from django.db import models
class UnsignedAutoField(models.AutoField):
    def db_type(self, connection):
        return 'integer UNSIGNED AUTO_INCREMENT'

    def rel_db_type(self, connection):
        return 'integer UNSIGNED'

然后在模型中使用它作为 id 字段:

id = UnsignedAutoField(primary_key=True)
© www.soinside.com 2019 - 2024. All rights reserved.