Odpowiedzi:
Pomocnik znacznika czasu jest dostępny tylko w create_table
bloku. Możesz dodać te kolumny, określając typy kolumn ręcznie:
class AddTimestampsToUser < ActiveRecord::Migration
def change_table
add_column :users, :created_at, :datetime, null: false
add_column :users, :updated_at, :datetime, null: false
end
end
Chociaż nie ma tej samej zwięzłej składni, co add_timestamps
metoda, którą określiłeś powyżej, Railsy nadal będą traktować te kolumny jako kolumny ze znacznikami czasu i normalnie aktualizować wartości.
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime
- skrót do wygenerowania migracji powyżej.
PG::NotNullViolation: ERROR: column "created_at" contains null value
ponieważ moja tabela zawiera już dane, które naruszają ograniczenie niepuste. Czy jest jakiś lepszy sposób na zrobienie tego niż najpierw usunięcie niezerowego ograniczenia, a następnie dodanie go później?
add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now
. Time.zone.now
to tylko przykład, powinieneś użyć dowolnej wartości, która ma sens dla twojej logiki.
Migracje to tylko dwie metody klas (lub metody instancji w 3.1): up
i down
(a czasami change
metoda instancji w 3.1). Chcesz, aby zmiany zostały wprowadzone do up
metody:
class AddTimestampsToUser < ActiveRecord::Migration
def self.up # Or `def up` in 3.1
change_table :users do |t|
t.timestamps
end
end
def self.down # Or `def down` in 3.1
remove_column :users, :created_at
remove_column :users, :updated_at
end
end
Jeśli jesteś w wersji 3.1, możesz również użyć change
(dzięki Dave):
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table(:users) { |t| t.timestamps }
end
end
Być może jesteś mylące def change
, def change_table
i change_table
.
Więcej informacji zawiera przewodnik migracji .
change
metoda, chociaż w tym przypadku nie problem :)
change
warto o tym wspomnieć, więc też to dodam.
Twój oryginalny kod jest bardzo zbliżony do właściwego, wystarczy użyć innej nazwy metody. Jeśli używasz Railsów 3.1 lub nowszych, musisz zdefiniować change
metodę zamiast change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def change
add_timestamps(:users)
end
end
Jeśli używasz starszej wersji, musisz zdefiniować up
i down
metody zamiast change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def up
add_timestamps(:users)
end
def down
remove_timestamps(:users)
end
end
@ user1899434 odpowiedział na fakt, że „istniejąca” tabela może oznaczać tabelę z już zawartymi rekordami, których możesz nie chcieć usunąć. Dlatego po dodaniu znaczników czasu z wartością null: false, która jest wartością domyślną i często pożądaną, wszystkie istniejące rekordy są nieprawidłowe.
Ale myślę, że odpowiedź można poprawić, łącząc dwa kroki w jedną migrację, a także używając bardziej semantycznej metody add_timestamps:
def change
add_timestamps :projects, default: Time.zone.now
change_column_default :projects, :created_at, nil
change_column_default :projects, :updated_at, nil
end
Możesz zastąpić inną sygnaturę czasową DateTime.now
, na przykład gdybyś chciał zamiast tego utworzyć / zaktualizować istniejące wcześniej rekordy.
Time.zone.now
tego należy użyć, jeśli chcemy, aby nasz kod działał zgodnie z właściwą strefą czasową.
Time.zone.now
która polega na zwróceniu instancji Time, która jest tworzona podczas uruchamiania migracji, i po prostu używa tego czasu jako domyślnego. Nowe obiekty nie otrzymają nowej instancji Time.
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table :users do |t|
t.timestamps
end
end
end
Dostępne są transformacje
change_table :table do |t|
t.column
t.index
t.timestamps
t.change
t.change_default
t.rename
t.references
t.belongs_to
t.string
t.text
t.integer
t.float
t.decimal
t.datetime
t.timestamp
t.time
t.date
t.binary
t.boolean
t.remove
t.remove_references
t.remove_belongs_to
t.remove_index
t.remove_timestamps
end
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
Odpowiedź Nicka Daviesa jest najbardziej kompletna pod względem dodawania kolumn sygnatur czasowych do tabeli z istniejącymi danymi. Jedynym minusem jest to, że wzrośnie ActiveRecord::IrreversibleMigration
nadb:rollback
.
Należy go tak zmodyfikować, aby działał w obu kierunkach:
def change
add_timestamps :campaigns, default: DateTime.now
change_column_default :campaigns, :created_at, from: DateTime.now, to: nil
change_column_default :campaigns, :updated_at, from: DateTime.now, to: nil
end
change_column_default
nie obsługuje from
iw to
tej wersji?), Ale wziąłem ten pomysł i stworzyłem up/down
metody zamiast jednej change
metody i działało to jak urok!
def change
add_timestamps :table_name
end
nie jestem pewien, kiedy dokładnie to zostało wprowadzone, ale w railsach 5.2.1 możesz to zrobić:
class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
def change
add_timestamps :my_table
end
end
aby uzyskać więcej informacji, zobacz „ Korzystanie z metody zmiany ” w dokumentacji migracji aktywnych rekordów.
, null: true
po:my_table
Zrobiłem prostą funkcję, że można zadzwonić, aby dodać do każdej tabeli (zakładając, że masz istniejącej bazy danych) created_at i updated_at dziedzinach:
# add created_at and updated_at to each table found.
def add_datetime
tables = ActiveRecord::Base.connection.tables
tables.each do |t|
ActiveRecord::Base.connection.add_timestamps t
end
end
add_timestamps (nazwa_tabeli, opcje = {}) public
Dodaje kolumny sygnatur czasowych (created_at i updated_at) do table_name. Dodatkowe opcje (takie jak null: false) są przekazywane do #add_column.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps(:users, null: false)
end
end
Odpowiedzi podane wcześniej wydają się prawidłowe, jednak napotkałem problemy, jeśli moja tabela zawiera już wpisy.
Otrzymałbym „BŁĄD: kolumna created_at
zawiera null
wartości”.
Aby naprawić, użyłem:
def up
add_column :projects, :created_at, :datetime, default: nil, null: false
add_column :projects, :updated_at, :datetime, default: nil, null: false
end
Następnie użyłem gem migracji_data, aby dodać czas dla bieżących projektów migracji, takich jak:
def data
Project.update_all created_at: Time.now
end
Wtedy wszystkie projekty utworzone po tej migracji zostaną poprawnie zaktualizowane. Upewnij się, że serwer również został zrestartowany, aby Railsy ActiveRecord
zaczęły śledzić sygnatury czasowe w rekordzie.
Wiele odpowiedzi tutaj, ale swoją też opublikuję, ponieważ żadna z poprzednich naprawdę nie zadziałała :)
Jak niektórzy zauważyli, #add_timestamps
niestety dodaje null: false
ograniczenie, co spowoduje, że stare wiersze będą nieprawidłowe, ponieważ nie zawierają tych wartości. Większość odpowiedzi sugeruje, że ustawiliśmy jakąś domyślną wartość ( Time.zone.now
), ale nie chciałbym tego robić, ponieważ te domyślne znaczniki czasu dla starych danych nie będą poprawne. Nie widzę wartości dodawania nieprawidłowych danych do tabeli.
Więc moja migracja była po prostu:
class AddTimestampsToUser < ActiveRecord::Migration
def change_table
add_column :projects, :created_at, :datetime
add_column :projects, :updated_at, :datetime
end
end
Nie null: false
, żadnych innych ograniczeń. Stare wiersze będą nadal obowiązywały z created_at
as NULL
i update_at
as NULL
(do czasu wykonania aktualizacji wiersza). Nowe wiersze będą miały created_at
i updated_at
wypełnione zgodnie z oczekiwaniami.
Problem z większością odpowiedzi tutaj polega na tym, że jeśli domyślnie Time.zone.now
wszystkie rekordy będą miały czas uruchomienia migracji jako domyślny czas, co prawdopodobnie nie jest tym, czego chcesz. W szynach 5 możesz zamiast tego użyć now()
. Spowoduje to ustawienie sygnatur czasowych dla istniejących rekordów jako czasu uruchomienia migracji i jako czasu rozpoczęcia transakcji zatwierdzania dla nowo wstawianych rekordów.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps :users, default: -> { 'now()' }, null: false
end
end
Używanie Time.current
to dobry styl https://github.com/rubocop-hq/rails-style-guide#timenow
def change
change_table :users do |t|
t.timestamps default: Time.current
t.change_default :created_at, from: Time.current, to: nil
t.change_default :updated_at, from: Time.current, to: nil
end
end
lub
def change
add_timestamps :users, default: Time.current
change_column_default :users, :created_at, from: Time.current, to: nil
change_column_default :users, :updated_at, from: Time.current, to: nil
end
To proste, aby dodać znacznik czasu do istniejącej tabeli.
class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
def change
add_timestamps :custom_field_metadata
end
end
Wydaje się, że to czyste rozwiązanie w Railsach 5.0.7 (odkryłem metodę change_column_null):
def change
add_timestamps :candidate_offices, default: nil, null: true
change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
end
Jestem na szynach 5.0 i żadna z tych opcji nie działała.
Jedyną rzeczą, która działała, było użycie typu: timestamp, a nie: datetime
def change
add_column :users, :created_at, :timestamp
add_column :users, :updated_at, :timestamp
end
Napotkałem ten sam problem na Rails 5, próbując użyć
change_table :my_table do |t|
t.timestamps
end
Udało mi się ręcznie dodać kolumny sygnatur czasowych za pomocą następujących elementów:
change_table :my_table do |t|
t.datetime :created_at, null: false, default: DateTime.now
t.datetime :updated_at, null: false, default: DateTime.now
end