Python Django 模型字段默认基于同一模型中的另一个字段
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4380879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django Model Field Default Based Off Another Field in Same Model
提问by dr jimbob
I have a model that I would like to contain a subjects name and their initials. (The data is somewhat anonymized and tracked by initials.)
我有一个模型,我想包含一个主题名称及其首字母。(数据在某种程度上是匿名的,并由首字母跟踪。)
Right now, I wrote
此刻,我写了
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)
As indicated by the last line, I would prefer to be able to have the initials actually get stored in the database as a field (independent of name), but that is initialized with a default value based on the name field. However, I am having issues as django models don't seem to have a 'self'.
如最后一行所示,我希望能够将首字母作为字段(独立于名称)实际存储在数据库中,但使用基于名称字段的默认值进行初始化。但是,我遇到了问题,因为 Django 模型似乎没有“自我”。
If I change the line to subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials), I can do the syncdb, but can't create new subjects.
如果我将行更改为subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials),则可以执行同步数据库,但无法创建新主题。
Is this possible in django, having a callable function give a default to some field based on the value of another field?
这在 django 中是否可能,有一个可调用的函数会根据另一个字段的值为某个字段提供默认值?
(For the curious, the reason I want to separate my store initials separately is in rare cases where weird last names may have different than the ones I am tracking. E.g., someone else decided that Subject 1 Named "John O'Mallory" initials are "JM" rather than "JO" and wants to fix edit it as an administrator.)
(出于好奇,我想将我的商店首字母分开的原因是在极少数情况下奇怪的姓氏可能与我正在跟踪的姓氏不同。例如,其他人认为主题 1 名为“John O'Mallory”的首字母是“JM”而不是“JO”并希望以管理员身份修复编辑它。)
采纳答案by Elf Sternberg
Models certainly do have a "self"! It's just that you're trying to define an attribute of a model class as being dependent upon a model instance; that's not possible, as the instance does not (and cannot) exist before your define the class and its attributes.
模特当然也有“自我”!只是您试图将模型类的属性定义为依赖于模型实例;这是不可能的,因为在您定义类及其属性之前,实例不(也不能)存在。
To get the effect you want, override the save() method of the model class. Make any changes you want to the instance necessary, then call the superclass's method to do the actual saving. Here's a quick example.
要获得您想要的效果,请覆盖模型类的 save() 方法。对实例进行必要的更改,然后调用超类的方法进行实际保存。这是一个快速示例。
def save(self, *args, **kwargs):
if not self.subject_init:
self.subject_init = self.subject_initials()
super(Subject, self).save(*args, **kwargs)
This is covered in Overriding Model Methodsin the documentation.
这在文档中的覆盖模型方法中有介绍。
回答by Gabi Purcaru
I don't know if there is a better way of doing this, but you can use a signal handlerfor the pre_savesignal:
我不知道是否有这样做的更好的办法,但你可以使用一个信号处理器对于该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)
回答by bignose
Using Django signals, this can be done quite early, by receiving the post_initsignalfrom the model.
使用Django的信号,这可以很早就完成,通过接收的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(" ")))
The post_initsignal is sent by the class once it has done initialisation on the instance. This way, the instance gets a value for namebefore testing whether its non-nullable fields are set.
该post_init信号由类一旦完成初始化的实例发送。这样,实例name在测试是否设置了其不可为空的字段之前获得了一个值。
回答by Kurt Peek
As an alternative implementation of Gabi Purcaru's answer, you can also connect to the pre_savesignal using the receiverdecorator:
作为Gabi Purcaru答案的替代实现,您还可以pre_save使用receiver装饰器连接到信号:
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()
This receiver function also takes the **kwargswildcard keyword arguments which all signal handlers must take according to https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions.
**kwargs根据https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions,此接收器函数还采用所有信号处理程序必须采用的通配符关键字参数。

