我们有一个专注于时间线演变可视化的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”.
提前感谢任何意见和建议!
想象一下这样的数据:
(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之间有任何里程碑。如果今天运行SELECT(2015-10-29),则item3和item5应该在输出中。如果我们在2015-12-15运行SELECT,则item3和item5不能在输出中。
答案 0 :(得分:0)
您应该在日期中使用models.DateTimeField(default = timezone.now)并使用models.BooleanField来定义TODAY行为里程碑。
我想这更好:
class Milestone(models.Model):
lifecycle = models.ForeignKey(Lifecycle, null=True, blank=True)
date = models.DateTimeField(max_length=10, blank=True)
today = models.BooleanField(default=False)
name = models.CharField(max_length=100, blank=True)
next_era = models.ForeignKey(Era, null=True, blank=True)
答案 1 :(得分:0)
在DateTimeField
上反对arkadyzalko的建议,但会另外注意一些事项。
首先,我建议您阅读此documentation并重点关注范围类型。如果每个时代都达到一个范围(你在时代结束时提前知道)那么就可以很容易地添加索引来确定一个时代的内容 - 即问题是日期是否在一个范围内,你可以加入好。
所以从数据库设计的角度来看,我会看看
Django应该支持所有这些(尽管您可能必须自己做排除约束)。
作为一些日期范围查询的示例:
test=# select '[2011-01-01,2011-02-01)'::daterange @> '2011-01-15'::date;
?column?
----------
t
(1 row)
test=# select '[2011-01-01,2011-02-01)'::daterange @> '2011-01-1'::date;
?column?
----------
t
(1 row)
test=# select '[2011-01-01,2011-02-01)'::daterange @> '2011-02-1'::date;
?column?
----------
f
(1 row)
但这意味着您可以加入范围内的值,s
FROM dates JOIN epoch ON epoch.range @> dates.date
GiST索引还允许您使用索引查找执行此操作。