如何使用Postgres在Rails 5.2中设置日期的默认值?

时间:2018-09-09 03:49:06

标签: ruby-on-rails postgresql rails-activerecord

使用Rails 5.2和Postgres 9.6.8

我有一个日期字段,要与map(df, ~mean(is.na(.))) $line [1] 0 $col_0001 [1] 0.0109375 $col_0002 [1] 0.0140625 $col_0003 [1] 0.0125 $col_0004 [1] 0.009375 $col_0005 [1] 0.0140625 $col_0006 [1] 0.00625 $col_0007 [1] 0.003125 $col_0008 [1] 0.0078125 $col_0009 [1] 0.0078125 created_at分开存储,因为我不在乎时间甚至日期。我真的只想存储月份和年份。

我很好奇是否有办法做到这一点并提供默认值。

在研究这个问题时,我看到建议做的答案:

updated_at

但这不起作用。我尝试将列更改为直t.date :month_and_year, null: false, default: -> { 'NOW()' },但我没有设置默认值。

我想我可以将月份和年份分别存储为整数,但是我很好奇是否有人为此提出了一个好的解决方案。

2 个答案:

答案 0 :(得分:2)

:default选项最终成为数据库中表定义的一部分。所以这个:

t.date :month_and_year, null: false, default: -> { 'NOW()' }

成为这样的SQL:

create table ... (
  ...
  month_and_year date not null default now(),
  ...
)

ActiveRecord将使用表 中的默认值,如果它可以理解的话。如果您有一个简单的默认值,例如:

some_column integer default 6

然后,ActiveRecord将使用该默认值,因为它知道6是什么以及如何使用它。

在您的情况下,数据库中的默认值是对now()数据库函数的调用,但是AR不知道now()的含义,因此它不会在新数据库中设置默认值创建模型。该模型保存到数据库后,将没有month_and_year,因此数据库将在新行的month_and_year列中使用其默认值;当然,ActiveRecord对此一无所知,因此您必须调用reload才能从数据库中获取month_and_year值。

您该如何解决?您可以像这样使用after_initialize钩子:

after_initialize if: :new_record? do
  if(month_and_year.nil?)
    self.month_and_year = Date.today
  end
end

我做了足够的事情,以至于我引起了一个简单的担忧:

module RecordDefaults
  extend ActiveSupport::Concern

  module ClassMethods
    #
    # Examples:
    #
    #     use_defaults number: 6,
    #                  %i[time1 time2] => Time.method(:now),
    #                  array: [6, 11, 23, 42]
    #
    def use_defaults(defs)
      after_initialize if: :new_record? do
        defs.each do |attrs, value|
          if(value.respond_to?(:call))
            value = instance_exec(&value)
          else
            value = value.dup
          end
          attrs_without_values = ->(a) { send(a).nil? }
          use_the_default      = ->(a) { send("#{a}=", value) }
          Array(attrs).select(&attrs_without_values).each(&use_the_default)
        end
      end
    end
  end
end

然后:

class SomeModel < ApplicationRecord
  include RecordDefaults
  use_defaults month_and_year: Date.method(:today)
end

答案 1 :(得分:1)

我同意mu is too short在他的回答中所说的。但我想在这里补充一点,有一种方法可以通过ActiveRecord的新attribute API对其进行设置。

例如,如果您在month_and_year模型中有Post列,并且想要为此设置默认值,则可以如下编写:

class Post < ApplicationRecord
  attribute :month_and_year, :date, default: -> { Date.today }
end

以上内容将为您设置默认日期。参见示例:

Loading development environment (Rails 5.2.1)
2.5.1 :001 > post = Post.new(name: 'Post A', title: 'Post title', content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.')
 => #<Post id: nil, name: "Post A", title: "Post title", content: "Lorem Ipsum is simply dummy text of the printing a...", created_at: nil, updated_at: nil, month_and_year: "2018-09-09">
2.5.1 :002 > post.save
   (0.2ms)  BEGIN
  Post Create (0.8ms)  INSERT INTO "posts" ("name", "title", "content", "created_at", "updated_at", "month_and_year") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["name", "Post A"], ["title", "Post title"], ["content", "Lorem Ipsum is simply dummy text of the printing and typesetting industry."], ["created_at", "2018-09-09 09:52:57.262764"], ["updated_at", "2018-09-09 09:52:57.262764"], ["month_and_year", "2018-09-09"]]
   (2.0ms)  COMMIT
 => true
2.5.1 :003 > Post.last
  Post Load (0.5ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT $1  [["LIMIT", 1]]
 => #<Post id: 3, name: "Post A", title: "Post title", content: "Lorem Ipsum is simply dummy text of the printing a...", created_at: "2018-09-09 09:52:57", updated_at:"2018-09-09 09:52:57", month_and_year: "2018-09-09">
2.5.1 :004 >