我想重命名 timestamp.rb 中定义的时间戳列。timestamp.rb 的方法可以被覆盖吗?以及在使用具有覆盖方法的模块的应用程序中必须做什么。
10 回答
我认为 NPatel 的方法是一种正确的方法,但如果您只需要在单个模型上更改 #created_at,它就做得太多了。由于 Timestamp 模块包含在每个 AR 对象中,因此您可以按模型覆盖它,而不是全局覆盖它。
<= 导轨 5.0
class User < ActiveRecord::Base
...
private
def timestamp_attributes_for_create
super << :registered_at
end
end
导轨 ~> 5.1
private_class_method
def self.timestamp_attributes_for_create
# only strings allowed here, symbols won't work, see below commit for more details
# https://github.com/rails/rails/commit/2b5dacb43dd92e98e1fd240a80c2a540ed380257
super << 'registered_at'
end
end
这可以通过重写 ActiveRecord::Timestamp 模块方法来完成。实际上并没有覆盖整个模块。我需要完成与使用遗留数据库相同的目标。我在 Rails 3 上,我已经开始遵循一种方法,如何通过重写 rails 功能来修补我的代码。
我首先创建了我正在使用的基础项目,并在初始化程序中创建了一个名为该文件的文件。在这种情况下,我创建了 active_record.rb。在文件中,我放置了代码来覆盖两个控制时间戳的方法。以下是我的代码示例:
module ActiveRecord
module Timestamp
private
def timestamp_attributes_for_update #:nodoc:
["updated_at", "updated_on", "modified_at"]
end
def timestamp_attributes_for_create #:nodoc:
["created_at", "created_on"]
end
end
end
注意:我还想提一下,这种让事情正常工作的猴子补丁是不受欢迎的,并且可能会在升级时中断,所以要小心并充分意识到你想要做什么。
更新:
- 每个api 更改将时间戳列名称从符号更改为字符串。感谢 Techbrunch 让我注意到这个 API 更改。
Rails >= 5.1的更新anwser 。我建议ApplicationRecord
在您的应用程序中使用默认可用的,并定义以下内容:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
class << self
private
def timestamp_attributes_for_create
super << 'my_created_at_column'
end
def timestamp_attributes_for_update
super << 'my_updated_at_column'
end
end
end
请注意,如果您不想为所有模型配置此模块,也可以在特定模型上使用此模块。
另请注意,您应该使用字符串而不是符号。
您可以使用 beforesave 和 beforecreate 方法将 DateTime.now 发布到您指定的列。
class Sample < ActiveRecord::Base
before_create :set_time_stamps
before_save :set_time_stamps
private
def set_time_stamps
self.created_column = DateTime.now if self.new_record?
self.updated_column = DateTime.now
end
end
快速丑陋的 hack,从 Milan Novota 的回答中详细说明:将以下内容附加到 environment.rb,用所需的列名替换 CREATED_COL 和 UPDATED_COL 常量的值:
module ActiveRecord
module Timestamp
CREATED_COL = 'created_at'
UPDATED_COL = 'updated_at'
private
def create_with_timestamps #:nodoc:
if record_timestamps
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
write_attribute(CREATED_COL, t) if respond_to?(CREATED_COL) && send(CREATED_COL).nil?
write_attribute(UPDATED_COL, t) if respond_to?(UPDATED_COL) && send(UPDATED_COL).nil?
end
create_without_timestamps
end
def update_with_timestamps(*args) #:nodoc:
if record_timestamps && (!partial_updates? || changed?)
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
write_attribute(UPDATED_COL, t) if respond_to?(UPDATED_COL)
end
update_without_timestamps(*args)
end
end
end
没有简单的方法可以做到这一点。您可以通过覆盖 ActiveRecord::Timestamp 模块或编写您自己的模块来为您实现这一点。
这是魔术的工作原理。
在 rails 4.2 中执行此操作的正确语法
class User < ActiveRecord::Base
...
private
def timestamp_attributes_for_create
super << 'date_add'
end
def timestamp_attributes_for_update
super << 'date_update'
end
end
或者您可以创建自己的列 (DateTime),并在创建时手动设置 created_at 列,并在更新时设置 updated_at 列。这可能比上面的 hack 更容易。这就是我要做的。更好的是,更新 rails 以允许我们更改这些名称。
不使用猴子补丁的解决方案;reversible
在迁移期间使用该块:
class CreateTest < ActiveRecord::Migration
def change
create_table :test do |t|
end
# set the timestamp columns default to now()
reversible do |dir|
dir.up do
tables = [
:test
]
tables.each do |t|
execute <<-SQL
ALTER TABLE #{t.to_s}
add column created timestamp without time zone default now();
SQL
execute <<-SQL
ALTER TABLE #{t.to_s}
add column updated timestamp without time zone default now();
SQL
end
end
dir.down do
# no need to do anything, the rollback will drop the tables
end
end
end
end
在 Rails 6.0 中,实例方法timestamp_attributes_for_create
不再存在。这个方法被推到类方法级别,现在是私有的
https://apidock.com/rails/v6.0.0/ActiveRecord/Timestamp/ClassMethods/timestamp_attributes_for_create
此提交引入了此更改https://github.com/rails/rails/commit/77ff9a0adbd1318b45203a76c64561b115245603
您仍然可以覆盖self.timestamp_attributes_for_create
,但我强烈建议不要这样做,因为可以更改私有 API,而无需另行通知或更大的版本颠簸。
对于 Rails 6,我没有看到给定的“Rails 方式”来定义自定义created_at
/updated_at
列。