【发布时间】:2016-01-15 00:08:31
【问题描述】:
我们有一个专注于时间线演化可视化的 Django 应用程序。从概念上讲,我们有以下关系:
1 个具有 1 个或多个生命周期的项目(更多用于版本控制)
1 个生命周期有 0..n 个里程碑
1 里程碑是以 YYYY-MM-DD 形式的字符串或特殊标签“今天”形式存储的日期,这意味着每天变化的日期(动态 - 未说明日期,但直到今天某些状态有效 - 如果今天比下一个里程碑要小)。
数据的特点是对里程碑和它们之间的阶段有非常不同的解释。里程碑的数量也是多种多样的。然而,有接缝最多可使用 7 个里程碑。可以对生命周期记录的特征进行分组(具有相同含义的相同数量的里程碑)。
我们在 PostgreSQL 之上使用 Django,模型架构如下:
class Item(models.Model):
... other attributes
lifecycle_actual = models.IntegerField(null=True, default=-1, help_text="Selectable actual roadmap. Can be used to override the imported data. Use the ID of particular roadmap or -1 for the latest import.")
class Lifecycle(models.Model):
... other attributes
lifecycle_group = models.ForeignKey(LifecycleGroup, help_text="Vizualization group.")
date0 = models.CharField(max_length=10, blank=True)
date1 = models.CharField(max_length=10, blank=True)
date2 = models.CharField(max_length=10, blank=True)
date3 = models.CharField(max_length=10, blank=True)
date4 = models.CharField(max_length=10, blank=True)
date5 = models.CharField(max_length=10, blank=True)
date6 = models.CharField(max_length=10, blank=True)
item = models.ForeignKey(Item, null=True, blank=True)
def __unicode__(self):
return self.item.fullname
class LifecycleGroup(models.Model):
name = models.CharField(max_length=220, help_text="Name of the group")
era0_name = models.CharField(max_length=100, blank=True)
era1_name = models.CharField(max_length=100, blank=True)
era2_name = models.CharField(max_length=100, blank=True)
era3_name = models.CharField(max_length=100, blank=True)
era4_name = models.CharField(max_length=100, blank=True)
era5_name = models.CharField(max_length=100, blank=True)
era6_name = models.CharField(max_length=100, blank=True)
era0_start_name = models.CharField(max_length=100, blank=True)
era1_start_name = models.CharField(max_length=100, blank=True)
era2_start_name = models.CharField(max_length=100, blank=True)
era3_start_name = models.CharField(max_length=100, blank=True)
era4_start_name = models.CharField(max_length=100, blank=True)
era5_start_name = models.CharField(max_length=100, blank=True)
era6_start_name = models.CharField(max_length=100, blank=True)
era0_css_classes = models.CharField(max_length=150, blank=True)
era1_css_classes = models.CharField(max_length=151, blank=True)
era2_css_classes = models.CharField(max_length=152, blank=True)
era3_css_classes = models.CharField(max_length=153, blank=True)
era4_css_classes = models.CharField(max_length=154, blank=True)
era5_css_classes = models.CharField(max_length=155, blank=True)
era6_css_classes = models.CharField(max_length=156, blank=True)
def __unicode__(self):
return self.name
总体上它运行良好,但是我们在报告问题方面存在问题,例如:
哪些项目将在 2015 年 12 月达到特定特征的里程碑?
即使我们把模型代码改成这样:
class Item(models.Model):
... other attributes
lifecycle_actual = models.IntegerField(null=True, default=-1, help_text="Selectable actual roadmap. Can be used to override the imported data. Use the ID of particular roadmap or -1 for the latest import.")
class Lifecycle(models.Model):
... other attributes
# lifecycle group - not used anymore - have to duplicate info somehow in milestones
# lifecycle_group = models.ForeignKey(LifecycleGroup, help_text="Vizualization group.")
item = models.ForeignKey(Item, null=True, blank=True)
def __unicode__(self):
return self.item.fullname
class Milestone(models.Model):
lifecycle = models.ForeignKey(Lifecycle, null=True, blank=True)
date = models.CharField(max_length=10, blank=True)
name = models.CharField(max_length=100, blank=True)
next_era = models.ForeignKey(Era, null=True, blank=True)
impact = ... cca 4 choices
order = models.PositiveIntegerField()
class Era(models.Model):
name = models.CharField(max_length=100, blank=True)
css_classes = models.CharField(max_length=150, blank=True)
我们还有几个问题:
- 对于我们拥有的每个可视化查询,我们必须始终在生命周期下加入里程碑(接缝与此规范化相矛盾)
针对此类需求推荐的架构设计是什么?
- 里程碑日期字段中的动态日期“今天”
如何在数据库中存储动态(更改)日期,使其对 SELECTS 有效并与存储的静态日期相媲美?
所以我们可以这样做:
SELECT * FROM item, lifecycle, milestone
WHERE item.id = lifecycle.item AND milestone.lifecycle = lifecycle.id
AND milestone.impact = 'huge'
AND milestone.date between '2015-12-01' AND '2015-12-31'
- 我们希望增强“今天”控制字符串
所以我们可以像这样存储里程碑定义:
"today +365d" or "today -20d", resp. “YYYY-MM-DD<today<YYYY-MM-DD”.
提前感谢任何cmets,建议!
编辑
想象这样的数据:
(item lifecycle => milestone name: date, ...)
item1 => born: 2011-12-02,
decline: 2015-06-01,
end of life:2017-06-01
item2 => lifecycle check: 2015-08-01,
some significant milestone: 2017-09-01,
depreciation ends: 2019-04-15,
to be decommissioned: 2022-04-01
item3 => initiated: 2012-05-08,
life until at least: *today*,
end of life: not declared
item4 => initiated: 2012-05-08,
productive life until at least: *today +2 years*,
end of life: 2032-08-01
item5 => born: unknown but latest *today*,
end of life:2017-06-01
其中今天是正在进行的日期,即用户使用数据的未来每个当前日期。
假设我们应该选择在 2015-10-01 和 2015-12-01 之间具有任何里程碑的所有项目。如果我们今天 (2015-10-29) 运行 SELECT,则 item3 和 item5 应该在输出中。如果我们在 2015 年 12 月 15 日运行该 SELECT,则 item3 和 item5 不能在输出中。
【问题讨论】:
-
“如何在数据库中存储动态(更改)日期,使其对 SELECTS 有效并与存储的静态日期相媲美?” 通常,我希望看到这样的东西表示为数据库视图。视图可以将诸如“TODAY”之类的文本解析为 current_date(或其他)的值,并将结果转换为 DATE。客户端代码使用视图,而不是基表。
-
视图的有趣想法 - 但是我不确定如何通过 Django ORM 处理。我还假设必须每天重新创建视图。 (为了正确呈现 TODAY 数据。
-
这是一篇不错的文章,涵盖了 Django 中的视图:blog.rescale.com/using-database-views-in-django-orm
-
我添加了数据以便更好地了解问题。
标签: python django database postgresql database-design