Réponses:
L'assistant d'horodatage n'est disponible que dans le create_table
bloc. Vous pouvez ajouter ces colonnes en spécifiant les types de colonnes manuellement:
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
Bien que cela n'ait pas la même syntaxe laconique que la add_timestamps
méthode que vous avez spécifiée ci-dessus, Rails traitera toujours ces colonnes comme des colonnes d'horodatage et mettra à jour les valeurs normalement.
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime
- un raccourci pour générer la migration ci-dessus.
PG::NotNullViolation: ERROR: column "created_at" contains null value
car ma table contient déjà des données qui violent la contrainte non nulle. Une meilleure façon de faire cela que de supprimer la contrainte non nulle dans un premier temps, puis de l'ajouter plus tard?
add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now
. Time.zone.now
est juste un exemple, vous devez utiliser la valeur qui convient à votre logique.
Les migrations ne sont que deux méthodes de classe (ou méthodes d'instance dans 3.1): up
et down
(et parfois une change
méthode d'instance dans 3.1). Vous souhaitez que vos modifications soient intégrées à la up
méthode:
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
Si vous êtes dans 3.1, vous pouvez également utiliser change
(merci Dave):
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table(:users) { |t| t.timestamps }
end
end
Peut-être vous êtes confus def change
, def change_table
et change_table
.
Consultez le guide de migration pour plus de détails.
change
méthode maintenant, bien que dans ce cas, pas le problème :)
change
vaut la peine d'être mentionné, je vais donc l'ajouter aussi.
Votre code d'origine est très proche de la droite, il vous suffit d'utiliser un nom de méthode différent. Si vous utilisez Rails 3.1 ou version ultérieure, vous devez définir une change
méthode au lieu de change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def change
add_timestamps(:users)
end
end
Si vous utilisez une version plus ancienne, vous devez définir up
et des down
méthodes au lieu de change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def up
add_timestamps(:users)
end
def down
remove_timestamps(:users)
end
end
La réponse de @ user1899434 a retenu le fait qu'une table "existante" ici pourrait signifier une table contenant déjà des enregistrements, des enregistrements que vous ne voudriez peut-être pas supprimer. Ainsi, lorsque vous ajoutez des horodatages avec null: false, qui est la valeur par défaut et souvent souhaitable, ces enregistrements existants sont tous invalides.
Mais je pense que cette réponse peut être améliorée, en combinant les deux étapes en une seule migration, ainsi qu'en utilisant la méthode plus sémantique 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
Vous pouvez remplacer un autre horodatage DateTime.now
, par exemple si vous vouliez que les enregistrements préexistants soient créés / mis à jour à l'aube des temps à la place.
Time.zone.now
c'est ce qui devrait être utilisé si nous voulons que notre code obéisse au fuseau horaire correct.
Time.zone.now
laquelle il renverra l'instance Time qui est créée lorsque la migration est exécutée et utilise simplement cette heure comme valeur par défaut. Les nouveaux objets n'obtiendront pas de nouvelle instance Time.
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table :users do |t|
t.timestamps
end
end
end
Les transformations disponibles sont
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
La réponse de Nick Davies est la plus complète en termes d'ajout de colonnes d'horodatage à une table avec des données existantes. Son seul inconvénient est qu'il augmentera ActiveRecord::IrreversibleMigration
sur undb:rollback
.
Il doit être modifié ainsi pour fonctionner dans les deux sens:
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
cela ne prend pas en charge from
et to
dans cette version?), Mais j'ai pris cette idée et créé des up/down
méthodes au lieu d'une seule change
méthode et cela a fonctionné comme un charme!
def change
add_timestamps :table_name
end
Je ne sais pas exactement quand cela a été introduit, mais dans les rails 5.2.1, vous pouvez le faire:
class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
def change
add_timestamps :my_table
end
end
pour en savoir plus, consultez « Utilisation de la méthode de modification » dans la documentation sur les migrations d'enregistrements actifs.
, null: true
après le:my_table
J'ai créé une fonction simple que vous pouvez appeler pour ajouter à chaque table (en supposant que vous avez une base de données existante) les champs created_at et updated_at :
# 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 (nom_table, options = {}) public
Ajoute des colonnes d'horodatage (created_at et updated_at) à nom_table. Des options supplémentaires (comme null: false) sont transmises à #add_column.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps(:users, null: false)
end
end
Les réponses précédentes semblent correctes, mais j'ai rencontré des problèmes si ma table contient déjà des entrées.
J'obtiendrais «ERREUR: la colonne created_at
contient des null
valeurs».
Pour réparer, j'ai utilisé:
def up
add_column :projects, :created_at, :datetime, default: nil, null: false
add_column :projects, :updated_at, :datetime, default: nil, null: false
end
J'ai ensuite utilisé le gem migration_data pour ajouter le temps des projets en cours sur la migration tels que:
def data
Project.update_all created_at: Time.now
end
Ensuite, tous les projets créés après cette migration seront correctement mis à jour. Assurez-vous que le serveur est également redémarré afin que Rails ActiveRecord
commence à suivre les horodatages sur l'enregistrement.
Beaucoup de réponses ici, mais je posterai la mienne aussi car aucune des précédentes n'a vraiment fonctionné pour moi :)
Comme certains l'ont noté, #add_timestamps
ajoute malheureusement la null: false
restriction, ce qui rendra les anciennes lignes invalides car elles n'ont pas ces valeurs remplies. La plupart des réponses ici suggèrent que nous définissions une valeur par défaut (Time.zone.now
), mais je n'aimerais pas le faire car ces horodatages par défaut pour les anciennes données ne seront pas corrects. Je ne vois pas l'intérêt d'ajouter des données incorrectes à la table.
Ma migration était donc simplement:
class AddTimestampsToUser < ActiveRecord::Migration
def change_table
add_column :projects, :created_at, :datetime
add_column :projects, :updated_at, :datetime
end
end
Non null: false
, pas d'autres restrictions. Les anciennes lignes continueront d'être valides avec created_at
as NULL
et update_at
as NULL
(jusqu'à ce qu'une mise à jour soit effectuée sur la ligne). Les nouvelles lignes auront created_at
et seront updated_at
remplies comme prévu.
Le problème avec la plupart des réponses ici est que si vous par défaut, Time.zone.now
tous les enregistrements auront l'heure à laquelle la migration a été exécutée comme heure par défaut, ce qui n'est probablement pas ce que vous voulez. Dans les rails 5, vous pouvez utiliser à la place now()
. Cela définira les horodatages pour les enregistrements existants comme l'heure à laquelle la migration a été exécutée et comme l'heure de début de la transaction de validation pour les enregistrements nouvellement insérés.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps :users, default: -> { 'now()' }, null: false
end
end
L'utilisation Time.current
est un bon style 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
ou
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
Ceci est simple pour ajouter un horodatage dans une table existante.
class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
def change
add_timestamps :custom_field_metadata
end
end
Cela semble être une solution propre dans Rails 5.0.7 (découverte de la méthode 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
Je suis sur les rails 5.0 et aucune de ces options n'a fonctionné.
La seule chose qui fonctionnait était d'utiliser le type à être: timestamp et non: datetime
def change
add_column :users, :created_at, :timestamp
add_column :users, :updated_at, :timestamp
end
J'ai personnellement utilisé ce qui suit, et il a mis à jour tous les enregistrements précédents avec l'heure / la date actuelle:
add_column :<table>, :created_at, :datetime, default: Time.zone.now, null: false
add_column :<table>, :updated_at, :datetime, default: Time.zone.now, null: false
J'ai rencontré le même problème sur Rails 5 en essayant d'utiliser
change_table :my_table do |t|
t.timestamps
end
J'ai pu ajouter les colonnes d'horodatage manuellement avec les éléments suivants:
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