如何在Django中创建自增整数字段?

103

我正在为购物车创建一个Order模型,我需要创建一个在订单生成时自动递增的字段:

class Order(models.Model):
    cart = models.ForeignKey(Cart)
    add_date = models.DateTimeField(auto_now_add=True)
    order_number = models.IntegerField()
    enable = models.BooleanField(default=True)

如何使IntegerField自动递增?


默认情况下,将会有一个自动递增的 ID 字段。 - Pradeeshnarayan
使用 pre_save 信号来检测购物车的更改?你的操作顺序是什么? - laike9m
9个回答

142

In Django

1:Django模型类具有名为id的默认字段,该字段是自动递增字段。
2:您可以使用AutoField字段定义自己的自动递增字段。

class Order(models.Model):
    auto_increment_id = models.AutoField(primary_key=True)
    # 如果您不想使用django提供给您的默认字段"id",则将primary_key = True

数据库设计

+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table      | Create Table                                                                                                                                                  |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
  `auto_increment_id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`auto_increment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

如果您想使用Django的默认id作为递增字段。

class Order(models.Model):
    add_date = models.DateTimeField(auto_now_add=True)

数据库设计

+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table       | Create Table                                                                                                                                                    |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `add_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+

18
我需要这个用于整数字段。 - cjahangir
11
请注意,您不能拥有多个自动字段(AutoField),Django 默认为 id 字段使用一个自动字段,因此如果您需要记录 id 和一个不表示主键的自增整数字段(例如,在用于保留数据插入顺序的“Through” 模型中),则此解决方案无法使用。 - Mike 'Pomax' Kamermans
3
如果在模型中多次使用AutoField,它将不起作用。你会得到这样的错误信息:一个模型不能有多个AutoField - Julio Marins
AutoFields必须设置primary_key=True。 - Anshuman Kumar
@cjahangir AutoField是一个IntegerField。 - Ciske

24

在Django中,每个模型都会默认生成一个自增的id字段。但是,如果你想手动使用自增,只需在你的模型中指定 AutoField 字段即可。

在Django中,每个模型默认都有一个自增的id字段。但如果您需要手动指定自增字段,只需在模型中添加 AutoField 字段即可。

class Author(models.Model):
    author_id = models.AutoField(primary_key=True)

您可以在Django AutoField文档中详细了解关于Django自动字段的更多信息。


8
class Belly(models.Model):
    belly_id = models.AutoField(primary_key=True)
    belly_name = models.CharField(max_length=50)

******** 或 *********

class Belly(models.Model):
   belly_name = models.CharField(max_length=50)

区别在于:
第一个表的主键是 belly_id(指定为AutoField),而第二个表的主键是id(隐含)。
我认为不需要直接使用它;如果您没有指定,模型将自动添加主键字段。否则,请查看Django文档中关于AutoField的详细信息

8
你可以创建一个自动字段。这是相关文档
请记住,Django 不允许在一个模型中有多个自动字段,因为你的主键(默认情况下)已经有一个。所以你需要重写模型的保存方法,可能从表中获取最后插入的记录,然后递增计数器并添加新的记录。
请确保该代码是线程安全的,因为在多个请求的情况下,你可能会尝试为不同的新记录插入相同的值。

7

编辑:修复了代码错误,防止在数据库中没有YourModel条目时导致其无法运行。

经常提到应该使用 AutoField,当然,在可能的情况下应该使用它。

但是,如果您需要id从500开始或增加10等任何原因,则有合理的理由自己实现自动递增字段。

在您的models.py文件中:

from django.db import models

def from_500():
    '''
    Returns the next default value for the `ones` field,
    starts from 500
    '''
    # Retrieve a list of `YourModel` instances, sort them by
    # the `ones` field and get the largest entry
    largest = YourModel.objects.all().order_by('ones').last()
    if not largest:
        # largest is `None` if `YourModel` has no instances
        # in which case we return the start value of 500
        return 500
    # If an instance of `YourModel` is returned, we get it's
    # `ones` attribute and increment it by 1
    return largest.ones + 1

def add_ten():
    ''' Returns the next default value for the `tens` field'''
    # Retrieve a list of `YourModel` instances, sort them by
    # the `tens` field and get the largest entry
    largest = YourModel.objects.all().order_by('tens').last()
    if not largest:
        # largest is `None` if `YourModel` has no instances
        # in which case we return the start value of 10
        return 10
    # If an instance of `YourModel` is returned, we get it's
    # `tens` attribute and increment it by 10
    return largest.tens + 10


class YourModel(model.Model):
    ones = models.IntegerField(primary_key=True,
                               default=from_500)
    tens = models.IntegerField(default=add_ten)

我们如何在模型声明之前引用 YourModel? - sbmalik
1
因为YourModel继承自Django的model.Model,所以这是可能的。在应用程序启动时,Django首先加载模型类,从而使此操作起作用。 - evantkchong

2
你可以覆盖 Django 的 save 方法,具体说明请参考官方文档 official doc about it
修改后的代码如下:
class Order(models.Model):
    cart = models.ForeignKey(Cart)
    add_date = models.DateTimeField(auto_now_add=True)
    order_number = models.IntegerField(default=0)  # changed here
    enable = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        self.order_number = self.order_number + 1
        super().save(*args, **kwargs)  # Call the "real" save() method.

另一种方法是使用信号。更多信息如下:

  1. 关于 pre-save 的官方 Django 文档
  2. 使用 pre-save 信号的 Stack Overflow 示例

1
我认为这不会起作用,它将始终为1,我猜你应该查询最后一个order_number并根据该数字递增。 - Fathy

1

如果您不打算使用自增字段作为主键,可以定义一个整数字段,并在save()方法中更新该整数字段。

class Order(models.Model):
    cart = models.ForeignKey(Cart)
    add_date = models.DateTimeField(auto_now_add=True)
    order_number = models.IntegerField()
    enable = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        orders = Order.objects.all()

        if orders.exists() and self._state.adding:
            last_order = orders.latest('order')
            self.order = int(last_order.order) + 1
        super().save(*args, **kwargs)

如果我们不在这里使用self._state.adding,那么在更新过程中订单也会自动增加。我们只查询想要在创建过程中增加订单的self._state.adding。


1
我需要的是:一个固定位数的整数文档编号,同时也像AutoField一样工作。
我的搜索带我到了各个地方,包括这个页面。 最终我做了类似于这样的事情:
我创建了一个表,其中包含一个名为DocuNumber的IntegerField字段,具有以下属性:
  • max_length=6
  • primary_key=True
  • unique=True
  • default=100000
max_length值可以根据需要设置(因此相应的default=值也要相应更改)。
在创建所述模型时会发出警告,但我可以忽略它。
之后,创建了一个文档(虚拟),然后如预期地,文档具有整数字段值100000。
之后将模型关键字段更改为:
  • 将字段类型更改为:AutoField
  • 去掉了max_lengthdefault属性
  • 保留了primary_key = True属性

接下来创建的(期望的)文档值为100001,后续数字逐个递增。

到目前为止还不错。


我认为一旦您重置索引,这将会出现错误。 - Ladenkov Vladislav

0

您可以使用默认的主键(id),它会自动递增。

注意:当您使用第一种设计即使用默认字段(id)作为主键时,请通过指定列名来初始化对象。 例如:

class User(models.Model):
    user_name = models.CharField(max_length = 100)

然后进行初始化。

user = User(user_name="XYZ")

如果你按照以下方式进行初始化:

user = User("XYZ")

然后Python将尝试设置id =“XYZ”,这将导致数据类型错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接