我可以使用未签名的AutoField吗?

时间:2013-08-29 14:12:10

标签: django django-models

我希望我的模型的主键是无符号的。因此我做了这样的事情:

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

这会在生成的MySQL表中找到UNSIGNED列,我想要它。但是,我相信每次创建新对象时我都不会自动分配到id,是吗?这似乎需要使用AutoField。问题是,AutoField已签名。有没有办法创建一个未签名的AutoField

2 个答案:

答案 0 :(得分:3)

字段的实际类型在后端指定。在MySQL的情况下,后端是django.db.backends.mysqldjango/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下降时,它将继承其所有行为。

答案 1 :(得分:0)

编辑:为了清楚起见,我自己或Simanas编写的解决方案都不应该用于现实世界的项目中。我写这个作为一个例子,如果他们决定避免DBMS内置方式,那么应该去哪个方向,而不是作为一个准备好使用的完整模型。

我很抱歉写了一个答案,而不是对Simanas的帖子发表评论,但我没有很高的声望发帖,我觉得这是必要的,因为这个问题在'django autofield无符号整数上排名很高'关键字。

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

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,因为在数据库级别不会执行自动增量。

你几乎肯定不想这样做。