【问题标题】:How can I support AutoField(primary_key=False) in django?如何在 django 中支持 AutoField(primary_key=False)?
【发布时间】:2016-10-09 02:45:39
【问题描述】:

我需要添加一个 不是 主键的 autoinc 字段。我正在将一个使用自动递增字段的大型生产数据库迁移到models.UUIDField。我一直在进行分段迁移,现在我的所有关系都与两种字段类型重复。我已准备好进行主键交换,但不幸的是,我仍然需要为旧客户端保留自动递增整数字段,因为它已被弃用。

由于 django 不允许我使用 primary_key=False 定义自动字段(即使这在 db 层完全支持),我正在寻找一个简单的解决方案。我最初的策略是简单地将字段更改为models.BigIntegerField('GUID', db_index=True, null=True, unique=True),然后使用migrations.RunSQL 手动设置默认nextval('my_guid_seq'::regclass)。到目前为止一切顺利,除了没有。事实证明,由于我的 null=True 声明,ORM 层的 django 正在接管并插入 null,这将不允许数据库层的默认值来完成它的工作。

因为糟糕的设计,核心开发人员很快就拒绝了这个请求,我非常同意这一点,但也有非常有效的用例,例如这样。 https://code.djangoproject.com/ticket/8576

我是一个非常薄弱的​​ django 开发人员,所以我不想在 ORM 层陷入杂草元编程。根据定义,这是一种 hack,所以我正在寻找最简单、最有创意的解决方案来解决这个限制

【问题讨论】:

  • 从 'my_guid_seq'::regclass 看你正在使用 postgresql,你可以通过编辑迁移来使用 postgreql 序列。但是,如果您更新您的问题并提及您的真正目标,您可能会得到更好的答案。
  • @e4c5 我有点害怕标记 postgres,因为那部分工作得很好,我不想分散 postgres 社区的注意力。不幸的是,我的问题一直存在于 django 的 ORM 堆栈中。
  • 是的,如果你能解释一下这个练习的目的,我们可能会找到方法
  • 我的目标是降级我的 autoinc 主键并将我的 UUID 字段用作主键。我没有删除 autoinc 字段,只是将它们降级。是的,所有 fk 都已更新,是的,所有关系都由 UUID 和 autoincs 维护
  • 我的目标是第一句话。你是在问我为什么我要从有序大整数切换到 UUID?

标签: django autofield


【解决方案1】:

您可以继承 AutoField 并覆盖 _check_primary_key 方法。

from django.db.models.fields import AutoField
from django.db.models.fields import checks


class AutoFieldNonPrimary(AutoField):

    def _check_primary_key(self):
        if self.primary_key:
            return [
                checks.Error(
                    "AutoFieldNonPrimary must not set primary_key=True.",
                    obj=self,
                    id="fields.E100",
                )
            ]
        else:
            return []

AutoField源代码here

编辑:更新链接

【讨论】:

  • 那个错误检查是有原因的,绕过它很可能会导致问题。我对 Django ORM 的内部结构不够熟悉,无法预测后果可能是什么,但我会寻找另一种解决方法。
  • 如果不举例说明可能产生的后果,我认为这不足以抹黑他的回答。没有什么可以阻止 AUTO INCREMENT 功能在数据库层作为非主键,所以我无法预见问题。
  • 覆盖后,makemigrations 错误停止,但是当你真正尝试迁移时,我们会得到一个错误。当您使用 sqlmigrate 输出时,您甚至可以看到 AutoFieldNonPrimary 列将被标记为 PRIMARY,因此在数据库级别不允许创建两个主键!
  • 这发生在我身上一次,但我只是做了一个调整查询的hacky解决方案。糟糕的修复,但必须完成。
【解决方案2】:

我知道,将主键更改为 UUID 非常痛苦。因此我想到的简单且更好的解决方案是添加另一个本质上自动递增的整数字段。

这是我的解决方案:

class ModelName(models.Model):
    auto_inc_id = models.IntegerField()

然后覆盖保存模型:

def save(self, *args, **kwargs):
    self.object_list = ModelName.objects.order_by('auto_inc_id')
    if len(self.object_list) == 0:  # if there are no objects
        self.auto_inc_id = 1
    else:
        self.auto_inc_id = self.object_list.last().auto_inc_id + 1
    super(ModelName, self).save()
 

【讨论】:

  • 我认为 auto_inc_id + 1 如果调用 save() 次数过多会导致竞争条件。
【解决方案3】:

无法将此格式化为评论,但修改@Abhimanyu 的答案以使保存方法更简洁(并且只发出一个查询)。同型号属性:

class ModelName(models.Model):
    auto_inc_id = models.IntegerField()

这是模型上的保存方法:

def save(self, *args, **kwargs):
    self.auto_inc_id = ModelName.objects.all().count() + 1
    super(ModelName, self).save()

【讨论】:

  • 可能使您的 auto_inc_id 不连续的不良做法。建议不要这样做。
猜你喜欢
  • 1970-01-01
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 2020-05-24
  • 2020-09-07
  • 1970-01-01
相关资源
最近更新 更多