基于同一模型中另一个字段的 Django 模型字段默认值

我有一个模型,我想包含一个主题的名称和他们的首字母(他的数据是某种程度上匿名和跟踪的首字母)。

现在,我写了

class Subject(models.Model):


name = models.CharField("Name", max_length=30)
def subject_initials(self):
return ''.join(map(lambda x: '' if len(x)==0 else x[0],
self.name.split(' ')))
# Next line is what I want to do (or something equivalent), but doesn't work with
# NameError: name 'self' is not defined
subject_init = models.CharField("Subject Initials", max_length=5, default=self.subject_initials)

正如最后一行所指出的,我希望能够将首字母作为字段(独立于名称)实际存储在数据库中,但是这是使用基于名称字段的默认值进行初始化的。然而,我有问题,因为 django 模型似乎没有一个“自我”。

如果将该行更改为 subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials),则可以执行 syncdb,但不能创建新的主题。

在 Django 中,有一个可调用函数根据另一个字段的值为某个字段提供默认值,这可能吗?

(出于好奇,我之所以想把我的店名首字母单独分开,是因为在极少数情况下,奇怪的姓氏可能与我正在追踪的姓氏不同。例如,其他人决定将名为“ John O’Mallory”的实验对象1的首字母缩写改为“ JM”而不是“ JO”,并希望以管理员的身份对其进行编辑。)

58496 次浏览

我不知道是否有更好的方法,但你可以 使用信号处理器pre_save信号:

from django.db.models.signals import pre_save


def default_subject(sender, instance, using):
if not instance.subject_init:
instance.subject_init = instance.subject_initials()


pre_save.connect(default_subject, sender=Subject)

模特当然有“自我”!只是您试图将模型类的属性定义为依赖于模型实例; 这是不可能的,因为在定义类及其属性之前,实例不存在(也不可能存在)。

若要获得所需的效果,请重写模型类的 save ()方法。对实例进行任何必要的更改,然后调用超类的方法来执行实际的保存。举个简单的例子。

def save(self, *args, **kwargs):
if not self.subject_init:
self.subject_init = self.subject_initials()
super(Subject, self).save(*args, **kwargs)

Overriding Model Methods在文档中介绍了这一点。

使用 姜戈信号,通过从模型接收 post_init信号,这可以很早地完成。

from django.db import models
import django.dispatch


class LoremIpsum(models.Model):
name = models.CharField(
"Name",
max_length=30,
)
subject_initials = models.CharField(
"Subject Initials",
max_length=5,
)


@django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum)
def set_default_loremipsum_initials(sender, instance, *args, **kwargs):
"""
Set the default value for `subject_initials` on the `instance`.


:param sender: The `LoremIpsum` class that sent the signal.
:param instance: The `LoremIpsum` instance that is being
initialised.
:return: None.
"""
if not instance.subject_initials:
instance.subject_initials = "".join(map(
(lambda x: x[0] if x else ""),
instance.name.split(" ")))

一旦对实例进行了初始化,类就会发送 post_init信号。这样,实例在测试是否设置了其非空字段之前获取 name的值。

作为 加比 · 普卡鲁答案的替代实现,您还可以使用 receiver室内设计师连接到 pre_save信号:

from django.db.models.signals import pre_save
from django.dispatch import receiver




@receiver(pre_save, sender=Subject)
def default_subject(sender, instance, **kwargs):
if not instance.subject_init:
instance.subject_init = instance.subject_initials()

这个接收器函数还接受所有信号处理程序根据 https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions必须接受的 **kwargs通配符关键字参数。