差不多 7 年前我问过这个问题,现在仍然偶尔有活动。
因此,对于那些像我一样思考这个话题的(初级)开发人员,我想补充一些想法。
我认为这个问题试图解决多个问题。第一个问题是 PostgreSQL 没有用于存储持续时间的本机数据类型(至少不是我熟悉的那种)。
因此,在处理持续时间时,我们被迫在持续时间和 PostgreSQL 提供的数据类型之间进行转换。就我而言,我使用了整数类型。
所以有两个后续问题。
在没有本机持续时间类型的情况下,我应该使用什么数据类型?
我会说整数或小数。这取决于应用程序的需求和数据的质量。如果您喜欢存储为分钟,但需要(毫)秒精度,请使用小数。但总的来说,我发现使用整数更容易,所以最好使用一个足够小的单位,使整数足够精确。
我应该如何在应用程序中的数字类型(整数/小数)和持续时间类型之间进行转换?
根据您希望如何处理数据,我认为最好封装有关持续时间如何转换为数据库数据类型的知识,而不是将其泄漏到应用程序的视图(和其他地方)中。如果这些知识泄露,以后更改底层数据类型会变得更加困难——例如,如果 PostgreSQL/Rails 添加了更好的数据类型。
所以我可能会做一个价值类。这意味着创建一个使用数据库值初始化的持续时间类,但可以作为持续时间进行查询和修改。
简单的例子:
class Duration
attr_accessor :seconds
def initialize(seconds)
@seconds = seconds
end
def minutes
(seconds / 60.0).round
end
def minutes=(minutes)
self.seconds = minutes * 60
end
def hours
(seconds / (60.0 * 60)).round
end
def hours=(hours)
self.seconds = hours * 60 * 60
end
def to_s(unit = :minutes) # Valid values are :seconds, :minutes and :hours.
I18n.t("value_classes.duration.units.#{unit}", value: send(unit))
end
end
如果您将它与 ActiveRecord 类上的自定义访问器结合使用,那么您将获得相当可靠的封装。
class Movie < ApplicationRecord
def duration
Duration.new(duration_in_seconds) # Name of database attribute
end
def duration=(duration_instance)
self.duration_in_seconds = duration_instance.seconds
end
end
在一个视图中:
<p>Duration: <%= @movie.duration %></p>
您可能希望向 Duration 类添加一些额外的格式化方法,因此您可以根据设计者在特定视图中的需要在“127 分钟”、“2 小时 7 分钟”和“2:07”之间进行选择。
该解决方案还包含访问器,因此您的控制器可以执行以下操作
@movie.duration = Duration.new.tap do |duration|
duration.minutes = params[:movie][:duration_as_minutes]
end