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