Как я могу установить значение по умолчанию для даты в Rails 5.2 с Postgres? - PullRequest
0 голосов
/ 09 сентября 2018

Использование Rails 5.2 и Postgres 9.6.8

У меня есть поле даты, которое я хочу сохранить отдельно от created_at и updated_at, потому что меня не волнует время или даже день. Я просто хочу сохранить месяц и год.

Мне любопытно, есть ли способ сделать это и указать значение по умолчанию.

Исследуя этот вопрос, я увидел ответы, предлагающие сделать:

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

но это не работает. Я пытался изменить столбец на прямую datetime, но я никогда не получал значение по умолчанию.

Полагаю, я мог бы хранить месяц и год отдельно как целые числа, но мне любопытно, если бы кто-нибудь придумал для этого хорошее решение.

Ответы [ 2 ]

0 голосов
/ 09 сентября 2018

Я согласен с тем, что му слишком короткое сказано в его ответе. Но я хотел бы добавить здесь, что есть способ настроить его в 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 >
0 голосов
/ 09 сентября 2018

Параметр :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
...