如何在 Django 模型中违反约束时增加唯一时间戳?

作为使用 TimescaleDB 的一部分,它需要时间戳作为主键(SensorReading 中的时间),我需要处理不同传感器值使用相同时间戳的情况。一个优雅的解决方案可能是涂抹碰撞时间戳(在碰撞时添加一微秒)。


对于以下模型,如何以稳健且高性能的方式解决这个问题?


class Sensor(models.Model):

    name = models.CharField(max_length=50)


class SensorReading(models.Model):

    time = models.DateTimeField(primary_key=True, default=datetime.now)

    sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE)

    value = models.FloatField()

PS 这是一个解决方法,因为 Django 不支持复合主键。否则,可以将传感器和时间戳设置为复合主键。


潇湘沐
浏览 104回答 3
3回答

收到一只叮咚

为了使用 timescale db,我创建了一个虚拟主键字段,它会欺骗 Django 并将复合键值表示为单个 json 元组。

侃侃尔雅

我发现的一种解决方案是在模型保存调用周围使用 try/ except 块。尝试添加传感器读数,大多数情况下都会成功,但如果发生碰撞,请处理该错误。确保抛出的异常正是由于该冲突而引起的,然后将时间戳增加一微秒(最小分辨率)。然后重复尝试保存传感器读数。由于首先发生的碰撞概率较低,因此这几乎总是会成功。下面是经过测试的代码,用于递归地处理递增时间戳直到成功。class SensorReading(models.Model):    time = models.DateTimeField(primary_key=True, default=datetime.now)    sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE)    value = models.FloatField()    def save(self, *args, **kwargs):        self.save_increment_time_on_duplicate(*args, **kwargs)        def save_increment_time_on_duplicate(self, *args, **kwargs):        try:            super().save(*args, **kwargs)        except IntegrityError as exception:            if all (k in exception.args[0] for k in ("Key","time", "already exists")):                self.time = str(parse_datetime(self.time) + timedelta(microseconds=1))                self.save_increment_time_on_duplicate(*args, **kwargs)更稳健的实现还可能在中止之前添加最大尝试次数。

蝴蝶不菲

一般来说,我建议至少在某种程度上从 Django 中获取该传感器表的表管理,使用 psycopg2 或其他驱动程序进行插入,并在 上创建适当的主键,然后公开该表(或Sensor, time在它)作为一个 Django 模型,它可以从中读取,如果需要的话,也许可以使用连接。这些应该写入一次,因此您不必处理更新,这通常就是它需要非复合主键的原因。在某些时候 Django 应该真正开始支持复合主键,但遗憾的是他们没有。其他方法可能有效,但它们可能根本无法很好地扩展。因此,如果您关心摄取率,我可能会尝试不同的方法。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python