【问题标题】:How can a unique timestamp be incremented on constraint violation in a Django model?如何在 Django 模型中违反约束时增加唯一时间戳?
【发布时间】:2021-01-17 17:47:43
【问题描述】:

作为使用 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()

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

【问题讨论】:

    标签: python django orm timescaledb


    【解决方案1】:

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

    https://viewflow.medium.com/the-django-compositeforeignkey-field-get-access-to-a-legacy-database-without-altering-db-tables-74abc9868026

    您可以在此处查看代码示例 - https://github.com/viewflow/cookbook/tree/v2/timescale_db

    【讨论】:

    • 非常有趣的方法。正如您在帖子中提到的,重要的是要记住不支持迁移和批量删除。如果像石墨烯 GraphQL 这样的扩展能很好地配合它会很有趣
    【解决方案2】:

    我发现的一个解决方案是在模型保存调用周围使用 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)
    

    更健壮的实现还可能会在中止之前添加最大尝试次数。

    【讨论】:

      【解决方案3】:

      一般来说,我建议至少从 Django 中取出该传感器表的表管理,使用 psycopg2 或其他驱动程序进行插入,并在 Sensor, time 上创建一个适当的主键,然后公开表 (或者它上面的视图)作为一个 Django 模型,它可以从中读取,如果你需要的话,可能需要一个连接。这些应该写一次,因此您不必处理更新,这通常是它需要非复合主键的原因。不过,在某些时候 Django 应该真正开始支持复合主键,可惜他们不支持。

      其他方法可能有效,但它们可能根本无法很好地扩展。因此,如果您关心摄取率,我可能会尝试不同的方法。

      【讨论】:

        猜你喜欢
        • 2012-09-26
        • 2020-04-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-11
        • 2016-06-12
        • 2016-11-27
        • 2018-03-14
        • 2012-12-31
        相关资源
        最近更新 更多