我希望我的模型的主键未签名。因此我做了这样的事情:
class MyModel(models.Model):
id = models.PositiveIntegerField(primary_key=True)
这会在生成的 MySQL 表中得到一个
UNSIGNED
列,这是我想要的。但是,我相信每次创建新对象时我都不会自动分配给 id
,不是吗?这似乎需要使用 AutoField
来代替。问题是,AutoField
已签名。有没有办法创建一个无符号的AutoField
?
字段的实际类型是在后端指定的。对于 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
下降时,它将继承其所有行为。
编辑:需要明确的是,我自己或 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,因为自动增量不是在数据库级别完成的。
你几乎肯定不想这样做。
也许我的回答对某人有用。在 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)