覆盖模型保存方法以自动存储更新字段的先前值

时间:2016-04-17 15:08:51

标签: python django django-models django-admin

假设我有许多记录值的探测器。我想设置Django模型来表示探针以及它们记录的测量值。所以这样的事情会起作用:

class Probe(models.Model):
    name = models.CharField(max_length=256)

    def __unicode__(self):
        return u'<Probe: %s>' % self.name

class Observation(models.Model):
    probe = models.ForeignKey(Probe)
    obstime = models.DateTimeField()
    # the above field should be understood to represent the time in the world
    # represented by the measurement value recorded.  *not* the time at which
    # that value was written to the database.
    value = models.FloatField()

    class Meta:
        unique_together = (('probe', 'obstime'), )

    def __unicode__(self):
        tup = (self.probe.name,
               self.obstime.strftime('%Y%m%d-%H%M%S'),
               '%0.2f' % self.value)
        return u'<Observation: %s @ %s = %s>' % tup

但除此之外,我真的希望我的数据库和应用程序能够跟踪更多信息。特别是,我真的希望Observation模型有3个额外的字段:db_recording_timeprevious_valueprevious_db_recording_time。我希望用户直接尝试仅操作上面给出的条目,但让其他字段以预期的方式自动运行。我认为应该可以通过覆盖save类的Observation方法来实现,但我需要一些帮助!

所以说一个人从1月5日中午回到现场,坐在他们的电脑旁。他们希望记录一些(从未输入过的)数据,表明ProbeA在1月1日凌晨2点读取3.14。我只希望他们需要指定3.14和2AMJan1st,但我想要观察进入DB:

probe: ProbeA, 
obstime: 2AMJan1st, 
value: 3.14,
db_recording_time: NoonJan5th,
previous_value: Null, 
previous_db_reording_time: Null

然后,几个小时后(2 PM,5月5日),同一个人可以看看他们的笔记本并意识到“哎呀,我误读了笔记本中的价值...它真的是2.14”。所以我希望他们(使用管理员或Python控制台)调用现有观察并将3.14更正为2.14。当他们这样做时,我希望DB中的观察显示:

probe: ProbeA, 
obstime: 2AMJan1st, 
value: 2.14,
db_recording_time: 2PMJan5th,
previous_value: 3.14, 
previous_db_reording_time: NoonJan5th

我觉得这应该非常简单,只需在Admin界面中组合使用只读字段,并在save类的Observation方法中覆盖一些合理的覆盖。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

您可以在保存之前从数据库中检索旧记录:

recording_time = models.DateTimeField(auto_now=True)
previous_value = models.FloatField(null=True, default=None)
previous_recording_time = models.DateTimeField(null=True, default=None)

def save(self, force_insert=False, force_update=False, using=None,
         update_fields=None):
    try:  
        obj = Observation.objects.get(pk=self.pk)  
        # this is the record of this instance before editing
    except Observation.DoesNotExist:  # self.pk==None: this is a new Observation
        pass
    else:  #  this is indeed an update
        self.previous_value = obj.value
        previous_db_recording_time = obj.db_recording_time
    super(Observation, self).save(force_insert=force_insert, force_update=force_update, using=using,
         update_fields=update_fields)

现在,没有人必须手动设置recording_timeprevious_valueprevious_recording_time。因此,您可以从ModelForm Observation课程中使用的Admin中排除这些字段。

答案 1 :(得分:0)

正如我在评论中建议的那样,您可以在观察模型中向self添加一个外键,该外键是指之前进行的观察,然后您不需要复制这些字段,例如previous_valueprevious_db_recording_time

class Observation(models.Model):
    ...
    prev_observation = models.ForeignKey('self', null=True)

    # you can also add a `is_recent` field to this model so you set it to True when its created
    is_recent = models.BooleanField(default=True)

因此,我们假设您在一周[o1, o2, o3, o4, o5, o6, o7]中添加了7个观察值,因此对于所有这7个is_recentt都将为True。

而且,您可以按如下方式获得这些观察结果:

Observation.objects.filter(probe=chosenprobe, 
                           obstime__gte=start, obstime__lte=end,
                           is_recent=True)

# [o1, o2, o3, o4, o5, o6, o7]

现在,您已使用o1o3o6更正了o8o9o10,所以此时您可以设置{ {1}},is_recento1为{1}}。

然后,再次运行上述查询将为您提供更新的(最近的)观察结果:

o3