【问题标题】:Django Models, Gathering models.AutoField number while overriding save() methodDjango 模型,在覆盖 save() 方法时收集模型。AutoField 编号
【发布时间】:2019-06-10 18:30:36
【问题描述】:

我有一个用于 Users 的 Django 模型,然后是一个用于 AccessLevels 的独立模型。

当使用 Django 的 ORM 保存 User 时,我还想在 AccessLevels 表中放置一个条目。

我的问题是 AccessLevelsPKUsersPK,我似乎无法覆盖save() 方法并收集id = models.AutoField(primary_key=True) 的值。

这是我的相关代码:

class Users(models.Model):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    # pk
    id = models.AutoField(primary_key=True)

    # client_id
    client_id = models.IntegerField(null=False, blank=False, default="-1")

    ...

    def save(self, *args, **kwargs):
        AccessLevel(user_id=self.id,
                client_id=self.client_id).save(using='default')

        print('Access Level Set for User while saving')
        super(Users, self).save(*args, **kwargs)

这是AccessLevel

的重要部分
class AccessLevel(models.Model):

    # pk
    id = models.AutoField(primary_key=True)

    # this is the id for the user, but I get null value
    user_id = models.IntegerField(null=False, blank=False, default="-1")

    client_id = models.IntegerField(null=False, blank=False, default="-1")

当我调用此代码时,我得到了错误。

root_user = Users(username='root',
                      email='root',
                      first_name='root',
                      last_name='root',
                      phone='root',
                      password=make_password(default_root_pw),
                      type='root').save(using='default')

这是错误

django.db.utils.IntegrityError:“user_id”列中的空值违反非空约束 详细信息:失败行包含 (3, null, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)。强>

【问题讨论】:

  • 不要那样做!主键通常被视为“黑盒”。是的,这些是数字,但旨在协调两个主键确实是在找麻烦。此外,“分发”主键的不是 Django,而是数据库本身。这保证了分发的主键是唯一的(当然,除非数据库有问题)。
  • 此外,您应该使用ForeignKeys 来关联另一个对象,而不是IntegerFields。 ForeignKeys 将为数据库添加额外的约束,以保证此类列中的值始终引用具有此类主键的(现有)记录。
  • 不,这会导致更严重的反模式:数据重复。最好使用ForeignKeys。 Django 还为此为程序员提供了便利,因为您相关的对象是延迟加载到内存中的。

标签: python django django-models model


【解决方案1】:

首先,绝对不要将 IntegerFields 用于这些值。它们是外键,您应该使用 ForeignKey 字段。这些应该被称为 userclient - 请注意,您仍然可以像以前一样访问基础 ID。

(此外,不需要显式声明id 字段;并且您几乎不应该在模型中覆盖__init__,但尤其是永远不会覆盖仅调用超级方法的方法。只需删除那个。)

class AccessLevel(models.Model):
    user = models.ForeignKey('Users', null=False, blank=False)
    client = models.ForeignKey('Client', null=False, blank=False)

class Users(models.Model):
    client = models.ForeignKey('Client', null=False, blank=False)

现在,对于您的实际问题,id 由数据库分配,直到保存后才会发生。所以你应该在之后创建你的相关项目。

def save(self, *args, **kwargs):
    super(Users, self).save(*args, **kwargs)
    AccessLevel(user_id=self.id,
            client_id=self.client_id).save(using='default')

    print('Access Level Set for User while saving')

另一种创建项目的方法可能更清楚一点,是使用外键提供的访问器:

self.accesslevel_set.create(client_id=self.client_id)

不过,最后,这可能比 a post-save signal 更好,而不是保存方法。

【讨论】:

  • 嗯,好的,明白了。感谢您的洞察力!
  • 只是想提醒任何偶然发现这个答案的人,Django 的 ORM 如何与外键一起工作非常酷。基本上,它将我的外键的 PK 存储在该列的新表中,但是当我在视图中收集对象时;我的结果对象包含来自外键约束的映射对象作为结果对象的属性(i.e. user.client_id = ClientObject)。它非常干净,我觉得我浪费了我的时间尝试手动执行此操作,我猜你生活并且你学习。再次感谢@Daniel Roseman :)
猜你喜欢
  • 1970-01-01
  • 2021-11-21
  • 2012-02-21
  • 2011-09-28
  • 2021-04-10
  • 2020-06-14
  • 1970-01-01
  • 2016-02-01
  • 1970-01-01
相关资源
最近更新 更多