Jak ustawić domyślną wartość kolumny typu data i godzina, aby rejestrować czas tworzenia podczas migracji?


114

Rozważ poniższy skrypt tworzenia tabeli:

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end

Czy można ustawić domyślną wartość jako aktualny czas?

Próbuję znaleźć niezależny odpowiednik DB w szynach dla definicji kolumn SQL podanych poniżej:

Składnia Oracle

start_at DATE DEFAULT SYSDATE() 

Składnia MySQL

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

LUB

start_at DATETIME DEFAULT NOW()

Odpowiedzi:


174

Jest to teraz obsługiwane w Railsach 5.

Oto przykładowa migracja:

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }
      t.timestamps
    end
  end 
end

Zobacz dyskusję na https://github.com/rails/rails/issues/27077 i odpowiedz tam przez prathamesh-sonpatki


14
Świetna odpowiedź. Na marginesie, pamiętaj, że w Postgres CURRENT_TIMESTAMPbędzie to czas rozpoczęcia bieżącej transakcji, więc wiele rekordów utworzonych w tej samej transakcji otrzyma tę samą wartość. Jeśli chcesz mieć aktualny czas wykonania instrukcji (ignorując kontekst transakcji), sprawdź CLOCK_TIMESTAMP.
— Abe Voelker

Dodałem coś takiego: add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }i tak wygląda w schema.rb t.datetime "start_date", default: -> { "now()" }Ale kiedy utworzyłem nowy rekord, nie jest on zapełniany. Każdy pomysł, dlaczego?
— Sandip Subedi

@SandipSubedi to samo dla mnie. Na szynach 5.2.3.
— courtimas

1
@courtsimas, które musisz zrobić, post.reloadaby uzyskać te wartości. Wyjaśniono to tutaj: stackoverflow.com/questions/53804787/…
— Sandip Subedi

1
@SandipSubedi Zrozumiałem, że 5 minut po moim komentarzu. Podobnie jak w przypadku generowania uuidów. Dzięki!
— courtimas

119

Możesz dodać funkcję w modelu takim jak ten:

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end

Aby model ustawił aktualny czas w modelu.

Możesz również umieścić w migracji kod sql, aby ustawić wartość domyślną na poziomie bazy danych, na przykład:

execute 'alter table foo alter column starts_at set default now()'

Ustawienie czegoś takiego:

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end

powoduje wykonanie funkcji Time.now podczas migracji, więc tabela w bazie danych jest tworzona w następujący sposób:

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');

ale myślę, że nie tego chcesz.


1
Obecnie ustawiam wartość w wywołaniu zwrotnym: before_create. <br> Szukałem tutaj jakiegoś rodzaju magii AR. Spędziłem trochę czasu przyglądając się kodowi Railsów, ale nie znalazłem żadnego rozwiązania. Pomyślałem, że zapytam, czy są jakieś alternatywy.
— Harish Shetty

Sugerowałbym zrobienie tego z wywołaniem zwrotnym na before_create.
— jonnii

1
Nie chcę zmieniać tabeli DB, ponieważ chcę, aby mój kod był neutralny. Miałem nadzieję, że AR ma jakiś mechanizm do ustawiania domyślnej wartości dla pola Datetime podobnego do pola created_at.
— Harish Shetty

9
Należy pamiętać, że wywołanie zwrotne before_create jest uruchamiane przed wstawieniem do bazy danych, ale po utworzeniu instancji obiektu (np new(). With ). Więc self.foo = Time.nowzastąpi wartość, której możesz nadać new(). Proponuję self.foo = Time.current unless self.foo.present?zamiast tego.
— Fatih

2
Nie to, że podczas korzystania z execute, spowoduje to umieszczenie linii :starts_at, :default => 'now()'w schema.rb. Jednak to nie zadziała, gdy użyjesz, rake db:schema:dumpktóry zastąpi schema.rb :starts_at, :default => '2015-05-29 09:46:33'(lub inną datą uruchomienia skryptu) ... smutno ...
— astreal

13

Moduł Active Record automatycznie tworzy i aktualizuje znaczniki czasu operacji, jeśli tabela zawiera pola o nazwie created_at/ created_onlub updated_at/ updated_on. Źródło - api.rubyonrails.org

Nie musisz robić nic więcej poza posiadaniem tej kolumny.


Mam już te pola w mojej tabeli. Potrzebuję dodatkowego pola, aby mój harmonogram mógł przechowywać datę rozpoczęcia. Obecnie używam: before_create callback do ustawienia aktualnej daty. Jeśli często napotykam ten scenariusz, muszę uciec się do napisania wtyczki, aby zmienić domyślną obsługę wartości w metodzie „to_sql” klasy ColumnDefinition.
— Harish Shetty

10

Zwyklę robię:

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end

Więc twój schema.rbbędzie miał coś takiego:

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end

Wada: to rozwiązanie działa tylko po uruchomieniu rake db:migrate, a nie po załadowaniu pliku schematu z czymś w rodzaju rake db:schema:load.
— Alter Lagos

9

Szukałem podobnych rozwiązań, ale skończyłem na https://github.com/FooBarWidget/default_value_for .

default_value_forWtyczka pozwala zdefiniować domyślne wartości dla modeli ActiveRecord w sposób deklaratywny. Na przykład:

class User < ActiveRecord::Base
  default_value_for :name, "(no name)"
  default_value_for :last_seen do
    Time.now
  end
end

u = User.new
u.name       # => "(no name)"
u.last_seen  # => Mon Sep 22 17:28:38 +0200 2008

8

Jeśli trzeba zmienić to istniejące kolumny DateTime w Rails 5 (zamiast tworzenia nowej tabeli, jak określono w innych odpowiedzi) tak, że może skorzystać z możliwości domyślna data, można utworzyć migrację takiego:

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end

Zły formularz, aby głosować na coś negatywnego i nie wyjaśniać, jaki problem został wzięty z odpowiedzią. Czy ktoś ma pomysł, dlaczego ten głos został odrzucony? Wyświetla składnię, jeśli chcesz zmienić kolumnę zamiast ją tworzyć.
— Matt Long

Nie jestem tym, który przegłosował, ale mam trudności ze zrozumieniem, jak ta odpowiedź różni się od odpowiedzi testamentu, która poprzedza Twoją o rok. Pytanie dotyczy ustawienia wartości domyślnej, a Twoja odpowiedź zawiera taką samą klauzulę lambda.
— nurettin

2
@nurettin Rozumiem, ale na moją obronę składnia tworzenia nowej kolumny jest nieco inna niż zmiana istniejącej kolumny. Zapewnienie takiej składni dla tych, którzy znaleźli to pytanie poprzez wyszukiwanie w Internecie, próbując dodać wartość domyślną do swojego obecnego modelu danych, zamiast tworzyć nowy model / tabelę, jest prawdopodobnie całkiem pomocne. Nie? Zakładasz, że wszyscy wiedzą, że mogą użyć tej samej lambdy dla kolumny change_column. Może powinni zdawać sobie z tego sprawę, ale dlatego odpowiedziałem tutaj - więc nie muszą nigdzie indziej, aby to zrozumieć. Twoje zdrowie!
— Matt Long

4
FWIW Właśnie użyłem tej składni i doceniam to, że w przypadku mojej wyszukiwarki Google i konkretnego problemu ta odpowiedź pomogła mi najbardziej.
— Jay Killeen

1
Nie widzę nic złego w tym, że jest to raczej odpowiedź niż komentarz. Głosowano za. Myślę, że ci, którzy przegrywają, po prostu czytają niedbale (jak większość zamykających pytania!; P).
— ikonoklast

-1

W odpowiedzi udzielonej przez @ szymon-lipiński (Szymon Lipiński) metoda execute nie zadziałała. Zgłaszał błąd składni MySQL.

Oto składnia MySQL, która działała dla mnie.

execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"

Tak więc ustawienie domyślnej wartości kolumny z datą i godziną w skrypcie migracji można wykonać w następujący sposób:

def up
  create_table :foo do |t|
    t.datetime :starts_at, :null => false
  end

  execute "ALTER TABLE `foo` CHANGE `starts_at` `starts_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
end
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.