Django 非主键自动字段

问题描述 投票:0回答:4

我们正在迁移 Oracle 数据库并进行必要的更改,一项重大更改是我们向所有模型添加

UUIDField
作为主键(对客户端隐藏),并且(尝试添加)常规
AutoField
.

我们发现直接向客户显示primary_key并不是一个好的设计,但他们也要求显示ID字段以更轻松地引用对象,但Django通过不允许

AutoField
不是primary_key

来限制这一点

这个问题有解决方法吗?

python django django-models
4个回答
15
投票

我认为可行的是使用

IntegerField
(几乎与
AutoField
在幕后使用的一样),并在模型第一次保存时增加它(在将其放入数据库之前)。

我编写了一个示例模型来在下面展示这一点。

from django.db import models

class MyModel(models.Model):

    # This is what you would increment on save
    # Default this to one as a starting point
    display_id = models.IntegerField(default=1)

    # Rest of your model data

    def save(self, *args, **kwargs):
        # This means that the model isn't saved to the database yet
        if self._state.adding:
            # Get the maximum display_id value from the database
            last_id = self.objects.all().aggregate(largest=models.Max('display_id'))['largest']

            # aggregate can return None! Check it first.
            # If it isn't none, just use the last ID specified (which should be the greatest) and add one to it
            if last_id is not None:
                self.display_id = last_id + 1

        super(MyModel, self).save(*args, **kwargs)

理论上,这只是复制了

AutoField
所做的事情,只是使用了不同的模型字段。


3
投票

假设所选的 DBMS 中没有序列支持,解决方案是创建模型:

class Counter(models.Model):
    count = models.PositiveIntegerField(default=0)

    @classmethod
    def get_next(cls):
        with transaction.atomic():
            cls.objects.update(count=models.F('count') + 1)
            return cls.objects.values_list('count', flat=True)[0]

并在数据迁移中创建它的一个实例。如果您使用事务管理,这可能会产生一些影响,但它(如果您的 DBMS 支持事务)保证始终返回下一个数字,无论事务开始时有多少对象以及是否已删除任何对象.


0
投票

您可以使用

serial
创建一个新字段,因为串行会在 postgres 中自动生成序列。

from django.db import connection
from django.db.models import BigIntegerField


class AutoIncrementalField(BigIntegerField):
    MAX_BIGINT = 9223372036854775807

    def __init__(self, *args, **kwargs):
        if kwargs.get("null"):
            raise ValueError("AutoIncrementalField cannot be null")
        if kwargs.get("default"):
            raise ValueError("AutoIncrementalField cannot have a default value")
        kwargs["editable"] = False
        super().__init__(*args, **kwargs)

    def formfield(self, **kwargs):
        return super().formfield(
            **{
                "min_value": 0,
                "max_value": BigIntegerField.MAX_BIGINT,
                **kwargs,
            }
        )

    def db_type(self, connection):
        return "bigserial"

    def pre_save(self, model_instance, add):
        if getattr(model_instance, self.attname) is None:
            app_name = model_instance._meta.app_label
            model_name = model_instance._meta.model_name
            attname = self.attname
            sequence_name = (
                f"{connection.schema_name}.{app_name}_{model_name}_{attname}_seq"
            )
            with connection.cursor() as cursor:
                cursor.execute(f"SELECT nextval('{sequence_name}')")
                sequence_value = cursor.fetchone()[0]
                setattr(model_instance, self.attname, sequence_value)
                return sequence_value
        return super().pre_save(model_instance, add)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        if "null" in kwargs:
            del kwargs["null"]
        if "blank" in kwargs:
            del kwargs["blank"]
        if "default" in kwargs:
            del kwargs["default"]
        return name, path, args, kwargs

现在在您的模型中使用此字段。

from django.db import models
class Book(models.Model):
    name = models.CharField(max_length=100, null=False, blank=False)
    serial_number = AutoIncrementalField()

这将自动递增并具有唯一性,直到并且除非您手动更改数据库中的序列。


-4
投票

您还可以使用计数作为自动增量。在我的项目中我就是这样使用的。

def ids():
    no = Employee.objects.count()
    if no == None:
        return 1
    else:
        return no + 1
emp_id = models.IntegerField(('Code'), default=ids, unique=True, editable=False)
© www.soinside.com 2019 - 2024. All rights reserved.