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

时间:2010-12-07 19:43:42

标签: python django django-models django-1.2

我有一个模型,我想包含一个主题名称和他们的首字母。 (数据有点匿名,并通过首字母进行跟踪。)

现在,我写了

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中使用,具有可调用函数根据另一个字段的值给出某个字段的默认值?

(好奇的是,我想分开我的商店首字母的原因是极少数情况下奇怪的姓氏可能跟我跟踪的不同。例如,有人认定主题1命名为“John O'Mallory “缩写是”JM“而不是”JO“,并希望修改为管理员。)

4 个答案:

答案 0 :(得分:72)

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

要获得所需的效果,请覆盖模型类的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中有所介绍。

答案 1 :(得分:14)

我不知道是否有更好的方法可以做到这一点,但use a signal handler可以the pre_save signal

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)

答案 2 :(得分:4)

使用Django signals,可以通过从模型中接收the post_init signal来尽早完成此操作。

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的值。

答案 3 :(得分:1)

作为Gabi Purcaru答案的替代实现,您还可以使用receiver decorator连接到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()

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