在使用 UUID 字段作为主字段的 Django 模型中添加非主键自动字段或“序列”字段
Posted
技术标签:
【中文标题】在使用 UUID 字段作为主字段的 Django 模型中添加非主键自动字段或“序列”字段【英文标题】:Adding a Non-Primary Key AutoField or a 'serial' field in a Django Model which uses a UUID field as a Primary Field 【发布时间】:2018-09-02 20:14:42 【问题描述】:我正在构建一个以 UUIDField 作为主键的模型。但我的用例需要。也有一个自动增量字段。 Django 提供了一个 AutoField。但它需要 primary_key=True 在我的情况下这是我不想要的,因为我使用 UUIDField 作为 primary_key。
我尝试创建一个字段并将 db_type 设置为“serial”并添加一个迁移,该迁移会更改在 100000 处重新启动的序列。使用 Admin 将对象添加到数据库将始终将数字字段存储为空值。如果我删除 null=True。然后保存将失败,因为它需要数字字段的值。
如何在保持 UUIDField 作为主键的同时使数字字段递增?
fields.py
class SerialField(models.Field):
description = _("BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE")
empty_strings_allowed = False
default_error_messages =
'invalid': _("'%(value)s' value must be an integer."),
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
super().__init__(*args, **kwargs)
def db_type(self, connection):
return 'serial'
models.py
from .fields import SerialField
class MyModel(models.Model):
uuid = models.UUIDField(
verbose_name=_("UUID Identifier"),
primary_key=True,
default=uuid.uuid4,
editable=False,
help_text=_("Requried, PrimaryKey none-editable"),
db_index=True,
)
number = SerialField(
primary_key=False,
editable=False,
help_text=_("Auto Increment Number"),
verbose_name=_("Number"),
#null=True
)
0002_auto_20180202.py
从 django.db 导入迁移
def forwards(apps, schema_editor):
if schema_editor.connection.alias == 'default':
return migrations.RunSQL(
"ALTER SEQUENCE app_name_mymodel_number_seq RESTART WITH 100000"
)
class Migration(migrations.Migration):
dependencies = [
('activities', '0001_initial'),
]
operations = [
migrations.RunPython(forwards)
]
【问题讨论】:
我没有答案,但您可以在this ticket 中看到一些历史记录(尽管讨论主要是关于拥有多个AutoFields
)。我同意这没有正确记录。
【参考方案1】:
为了举例,假设我们的表格名称是payouts
以下是支付模式:
class Payouts(models.Model):
# seq_id = models.IntegerField()
# we will create seq_id auto-increment by raw SQL
id = models.UUIDField(primary_key=True)
class Meta:
managed = True
db_table = 'payouts'
您可以看到我们希望 id 以 uuid 作为主键。
按照以下说明进行操作:
第 1 步:为上述模型制作迁移文件。
python manage.py makemigrations
第 2 步:生成一个空迁移以执行 SQL 查询:
python manage.py makemigrations --empty -n dont_delete_add_defaults_sql
步骤 3: 编辑 dont_delete_add_defaults_sql 如下:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('apiapp', '0001_initial'), #Replace 0001_initial with the name of file generated in step 1
]
operations = [
migrations.RunSQL("ALTER TABLE payouts ADD seq_id serial NOT NULL;"),
migrations.RunSQL("DROP SEQUENCE IF EXISTS payouts_seq_id_seq CASCADE;"),
migrations.RunSQL("create sequence payouts_seq_id_seq owned by payouts.seq_id;"),
migrations.RunSQL("alter table payouts alter column seq_id set default nextval('payouts_seq_id_seq');"),
]
第 4 步:运行最终迁移
python manage.py migrate
【讨论】:
【参考方案2】:也试过了。我的解决方法是改用原始 SQL。
迁移:
migrations.RunSQL(
"CREATE SEQUENCE sequence_name START 100000",
reverse_sql="DROP SEQUENCE IF EXISTS sequence_name",
elidable=False,
),
型号:
def get_next_increment():
with connection.cursor() as cursor:
cursor.execute("SELECT nextval('sequence_name')")
result = cursor.fetchone()
return result[0]
class MyModel(models.Model):
my_field = models.IntegerField(default=get_next_increment, editable=False, unique=True)
【讨论】:
以上是关于在使用 UUID 字段作为主字段的 Django 模型中添加非主键自动字段或“序列”字段的主要内容,如果未能解决你的问题,请参考以下文章
不能将 UUID 用作主键:Uuid:diesel::Expression 不满足 [重复]