【问题标题】:Use model method as default value for Django Model?使用模型方法作为 Django 模型的默认值?
【发布时间】:2016-05-29 09:46:57
【问题描述】:

我的模型中实际上有这个方法:

def speed_score_compute(self):
  # Speed score:                                                                                                     
  #                                                                                                                  
  #   - 8 point for every % of time spent in                                                                         
  #     high intensity running phase.                                                                                
  #   - Average Speed in High Intensity                                                                              
  #     running phase (30km/h = 50 points                                                                            
  #     0-15km/h = 15 points )

  try:
    high_intensity_running_ratio = ((
      (self.h_i_run_time * 100)/self.training_length) * 8)
  except ZeroDivisionError:
    return 0
  high_intensity_running_ratio = min(50, high_intensity_running_ratio)
  if self.h_i_average_speed < 15:
    average_speed_score = 10
  else:
    average_speed_score = self.cross_multiplication(
      30, self.h_i_average_speed, 50)
  final_speed_score = high_intensity_running_ratio + average_speed_score
  return final_speed_score

我想像这样将它用作我的模型的默认值:

speed_score = models.IntegerField(default=speed_score_compute)

但这不起作用(请参阅下面的错误消息)。我检查了不同的主题,例如 this one,但这仅适用于函数(不使用 self 属性)而不适用于方法(我必须使用方法,因为我正在使用实际的对象属性)。

Django doc. 似乎在谈论这个,但我不太清楚可能是因为我还是 Django 和 Programmation 的新手。

有没有办法做到这一点?

编辑:

我的功能在我的模型之上定义。但这是我的错误信息:

ValueError: Could not find function speed_score_compute in tournament.models. Please note that due to Python 2 limitations, you cannot serialize unbound method functions (e.g. a method declared and used in the same class body). Please move the function into the main module body to use migrations. For more information, see https://docs.djangoproject.com/en/1.8/topics/migrations/#serializing-values

错误信息很清楚,我似乎无法做到这一点。但是还有其他方法可以实现吗?

【问题讨论】:

  • 我的speed_score_compute()函数定义在speed_score模型之上。
  • “但这不起作用”意味着什么?错误?结果无效?
  • 问题已更新为我的错误消息。
  • 我有很多关于分数计算的方法,我只是在这里放一个以保持问题清楚。如果我删除activity_score_compute() 方法,将有与speed_score_compute() 相同的消息。我将编辑以将 speed_score 放在一个以避免混淆。
  • @Addict 我可以修改您的问题以使其更通用和规范,以便任何提出相同问题的人都可以轻松引用它吗?

标签: python django django-models django-rest-framework


【解决方案1】:

问题

当我们提供 default=callable 并提供来自模型的方法时,它不会被作为模型实例的 self 参数调用。

覆盖save()

我没有找到比覆盖MyModel.save()* 更好的解决方案,如下所示:

class MyModel(models.Model):
    def save(self, *args, **kwargs):
        if self.speed_score is None:
            self.speed_score = ...
            # or
            self.calculate_speed_score()

        # Now we call the actual save method
        super(MyModel, self).save(*args, **kwargs)

这样,如果您尝试保存模型,而该字段没有设置值,它会在保存之前填充。

就我个人而言,我只是更喜欢这种方法,即在模型中定义属于模型的所有内容(数据、方法、验证、默认值等)。我认为这种方法被称为胖 Django 模型方法

*如果你找到更好的方法,我也想学习一下!

使用pre_save 信号

Django 提供了一个pre_save 信号,该信号在save() 在模型上运行之前运行。信号同步运行,即 pre_save 代码需要在模型上调用 save() 之前完成运行。您将获得与覆盖 save() 相同的结果(和执行顺序)。

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


@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    instance = kwargs['instance']
    instance.populate_default_values()

如果您希望将默认值行为与模型分开,这种方法适合您!

什么时候调用save()?我可以在对象保存之前使用它吗?

好问题!因为我们希望能够在保存填充默认值之前使用我们的对象。

要获取一个对象,而不保存它,我们可以这样做:

instance = MyModel()

如果您创建它使用MyModel.objects.create(),那么save() 将被调用。它本质上是(see source code) 等价于:

instance = MyModel()
instance.save()

如果你感兴趣,你也可以定义一个MyModel.populate_default_values(),你可以在对象生命周期的任何阶段调用它(在创建、保存或按需,这取决于你)

【讨论】:

  • 我想知道 Django 进程中什么时候调用 save 方法?每次你创建一个新的对象或?...
  • 我认为s = SpeedScore() 不会调用save(),但SpeedScore.objects.create() 会,所以您可以在保存之前控制并拥有一个可以使用的对象。
  • 我会争辩一点,我认为pre_save 信号更好。它对save()的默认行为的影响较小,它还具有更好的接口,可以通过查看update_fieldsinstancesender等参数来检查对象状态。
  • 在这里,我将添加一个使用pre_save 的部分,为什么不呢:P
  • 谢谢@Sayse,添加了!
猜你喜欢
  • 2012-08-09
  • 1970-01-01
  • 2018-10-22
  • 1970-01-01
  • 2011-09-03
  • 2022-06-17
  • 1970-01-01
  • 1970-01-01
  • 2017-11-10
相关资源
最近更新 更多