Python Django for PostgreSQL中动态日期的推荐模式设计是什么?

Python Django for PostgreSQL中动态日期的推荐模式设计是什么?,python,django,database,postgresql,database-design,Python,Django,Database,Postgresql,Database Design,我们有一个Django应用程序,专注于时间线演化可视化。在这里,我们从概念上理解了以下关系: 1个具有1个或多个生命周期的项更多用于版本控制 1生命周期有0..n个里程碑 1里程碑是一个以YYYY-MM-DD格式存储的字符串或今天的特殊标记形式存储的日期,这意味着每日更改日期动态-日期未声明,但直到今天是某个有效状态-如果今天小于下一个里程碑 数据的特点是,对里程碑和它们之间的阶段有非常不同的解释。此外,里程碑的数量也是多种多样的。但是,最多可使用7个里程碑。生命周期记录的特征可以分组为具有相同

我们有一个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
"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年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) 
我们还有几个问题:

对于每个可视化查询,我们必须始终在生命周期下加入里程碑,因为我们必须与此规范化相矛盾 对于这种需求,推荐的模式设计是什么

里程碑日期字段中的动态今天日期 如何在数据库中存储动态更改日期,使其对选择有效并与存储的静态日期相比较

因此,我们可以:

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'
我们希望增强今天的控制字符串 因此,我们可以这样存储里程碑定义:

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
"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 today 2015-10-29,则项目3和项目5应在输出中。如果我们在2015-12-15运行该选择,则项目3和项目5不得出现在输出中。

您应该在日期中使用models.DateTimeFielddefault=timezone.now,并使用models.boolean字段定义今天的行为里程碑

我想这样更好:

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)

支持arkadyzalko关于DateTimeField的建议,但需要注意一些其他事项

首先,我建议阅读本文,重点关注范围类型。如果每个纪元到达一个你事先知道纪元何时结束的范围,那么就很容易添加索引来确定纪元中的内容,即问题是日期是否在某个范围内,你也可以加入该范围

因此,从数据库设计的角度来看

对纪元边界使用范围类型 使用排除约束以确保它们不重叠 在活动日期和纪元之间的重叠处加入。 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的值 从日期在epoch.range@>dates.date上加入epoch


GiST索引还允许您通过索引查找来实现这一点。

如何在数据库中存储动态更改日期,以便它对选择有效并与存储的静态日期相比较?通常,我希望看到这样的东西被表示为数据库视图。视图可以将“今天”之类的文本解析为当前_日期的值或其他值,还可以将结果转换为日期。客户机代码使用的是视图,而不是基表。视图的想法很有趣-我不确定如何通过Django ORM处理它。我还假设必须每天重新创建视图。为了正确呈现今日数据。这是一篇很好的文章,介绍了Django中的视图:我添加数据是为了更好地理解问题。有趣的方法,但我不明白这如何有助于做出所描述的选择语句。你能在你的建议中举一个调整选择的例子吗?或者你的想法是用两个选择来代替吗?第一个选择所有toady=True里程碑,如果现在对标准日期范围有效,第二个选择使用传统日期属性?我现在知道了。接缝必须是正确的方向。我已经在考虑优化,具体日期1001-01-01可以用作今天的行为,因此我将保存属性:如果您添加示例SELECT,我将检查您的答案作为解决方案。谢谢,我不知道范围类型!我是否正确理解,建议将模型的概念更改为:1生命周期具有0。。n年代?而不是当前的1生命周期有0。。n Milestonese您如何将今天作为范围日期的边界?使用None?好吧,PostgreSQL理解“today”是指now::date,所以一旦保存它,它就固定在保存它的日期。如果你愿意
一个在不确定的未来,然后使用时间戳而不是日期,在你的第一个问题中使用“无限”,在某种程度上是的。但关键的是,你最终会得到一个定义范围为“[2011-01-012011-02-01”的纪元,即从1月1日开始,直到但不包括2月1日。将发布第二条评论以显示查询。使用示例查询更新答案