I'm trying to add the time to my index page in the datatable but after migration the only time that shows up in the table on my index page is the time that the migration took place. I've tried many different ways but can't seem to find a way to display the time when a new form has been created in the application I'm working on. I am new to Ruby on Rails so a detailed explanation would be helpful.
class AddTimeToArticles < ActiveRecord::Migration
def change
add_column :articles, :time, :string, default: Time.now.strftime('%m-%e-%y %H:%M')
end
end
By default Rails migrations will include a timestamp helper which creates created_at and updated_at fields. Those are special fields that Rails will use and update as needed.
http://guides.rubyonrails.org/migrations.html
The timestamps macro adds two columns, created_at and updated_at.
These special columns are automatically managed by Active Record if
they exist.
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
However, if you don't have those fields in your table, you can add them via a migration too.
class AddTimestampsToProducts < ActiveRecord::Migration
def change
add_column :products, :created_at, :datetime
add_column :products, :updated_at, :datetime
end
end
Use database function like now():
default: 'now()'
Or use default rails column created_at
Column should be datetime, just pass null: false and Rails add time without you:
class AddTimeToArticles < ActiveRecord::Migration
def change
add_column :articles, :time, :datetime, null: false
end
end
=> a = Article.create(name: 'Foobar')
=> a.time
# Mon, 07 Jul 2014 15:08:37 UTC +00:00
Related
Trying to add Timestamps to existing table.
According to Api documenation add_timestamps
Here is my code in migration:
def change
add_timestamps(:products, null: false)
end
Getting error:
*-- add_timestamps(:products, {:null=>false})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: Cannot add a NOT NULL column with default value NULL: ALTER TABLE "products" ADD "created_at" datetime NOT NULL*
I've also tried all solution in this thread
Same error...
Rails 5.1.4
Ruby 2.4.0
You cannot add columns with not-null constraint to a non-empty table because the existing lines in the table would have empty values right away and therefore the condition fails.
Instead, introduce the columns in three steps:
def change
# add new column but allow null values
add_timestamps :products, null: true
# backfill existing records with created_at and updated_at
# values that make clear that the records are faked
long_ago = DateTime.new(2000, 1, 1)
Product.update_all(created_at: long_ago, updated_at: long_ago)
# change to not null constraints
change_column_null :products, :created_at, false
change_column_null :products, :updated_at, false
end
In my opinion, it is wrong to manipulate existing data with activerecord queries or even SQL in migrations.
The correct rails 5.2+ way to do this is :
class AddTimestampsToCars < ActiveRecord::Migration[5.2]
def change
add_timestamps :cars, null: false, default: -> { 'NOW()' }
end
end
It's a proc so you should be able to set a date in the past if you want to.
Source: https://github.com/rails/rails/pull/20005
I like #spickermann's approach since it takes into account the existing records and probably your migration already went all the way to production, his method ensures data perseverance.
Nevertheless, many of you guys might find yourselves in that situation, but still in development, meaning that there's no real sensitive data you might be afraid of losing... That gives you a bit more freedom on how you can perform the change in the table.
If your code and records only exist locally (if you still have no records created, just skip step 1.) and that table was created in the last migration , my suggestion is:
1.- Delete all the records from that table.
2.- Go to your migration file and edit it by adding t.timestamps so that it looks something like this:
class CreateInstitutionalLegals < ActiveRecord::Migration[5.0]
def change
create_table :institutional_legals do |t|
# Your original migration content goes here
.
.
t.timestamps # This is your addition
end
end
end
3.- Then go to your console and enter rails:db:redo. As explained here, that command is a shortcut for doing a rollback and then migrating back up again.
Now you will see that your schema is updated with the corresponding created_atand updated_at columns.
The concrete benefit of this is that it is super easy to do, you don't create an extra migration file and you learn to use a very handy command ;)
I'm on rails 5.0 and none of these options worked. The rails:db:redo will work but isn't a feasible solution for most.
The only thing that worked was
def change
add_column :products, :created_at, :timestamp
add_column :products, :updated_at, :timestamp
end
I had the same issue. I wanted the end result to be strictly equivalent to add_timestamps :products on an fresh database.
Instead of running a query to backfill, I ended up doing a 3-steps process.
add column with null allowed and default to current time to backfill
change constraint to not null
remove default
And it is reversible.
add_column :products, :created_at, :datetime, precision: 6, null: true, default: -> { "CURRENT_TIMESTAMP" }
add_column :products, :updated_at, :datetime, precision: 6, null: true, default: -> { "CURRENT_TIMESTAMP" }
change_column_null :products, :created_at, false
change_column_null :products, :updated_at, false
change_column_default :products, :created_at, from: -> { "CURRENT_TIMESTAMP" }, to: nil
change_column_default :products, :updated_at, from: -> { "CURRENT_TIMESTAMP" }, to: nil
NB: This is with Rails 6.1 and PostgreSQL
I recently added new columns to my database (sqlite) for Media and it shows that the columns are inserted, but the new columns will not update on Medium.new
My original database:
class CreateMedia < ActiveRecord::Migration
def change
create_table :media do |t|
t.string :name
t.string :location
t.timestamps
end
end
end
These columns update on Media.new
class AddMetaToMedia < ActiveRecord::Migration
def change
add_column :media, :ext, :string
add_column :media, :l_mod, :string
add_column :media, :d_create, :string
end
end
and I am calling
Medium.new(name: f, location: str, ext: ex)
ext will not update to ex = File.extname(f), which I know has a value through print statements/console. Am I calling Medium.new wrong? Why is it updating name and location but not the new columns?
edit: Here is my model, I've tried with and without attr_accessible/attr_accesor
class Medium < ActiveRecord::Base
attr_accessor :ext, :d_create, :l_mod
end
Mostly for those who find this later...may also need to whitelist new params in interested controllers
attr_accessor :ext, :d_create, :l_mod remove this line from your model and try again.
Now you have these attributes in DB so Rails will do this job automatically
I am using Redmine and when I run a rake db migration the database gets created in mysql ok. My problem is that the date field is wrong. I want it to be a mysql timestamp type, but instead it is a DATETIME type in MySQL.
class CreateChats < ActiveRecord::Migration
def self.up
create_table :chats do |t|
t.column :message, :string
t.column :user, :integer
t.column :sendDate, :timestamp
end
end
def self.down
drop_table :chats
end
end
In addition if I make a change to this migration how do I get it to remake the table (deleting it doesn't work)?
You can always insert a column with a custom type if you want. The symbol names are automatically converted into whatever ActiveRecord defines, but if you use a plain string it goes in as-is:
t.column :ar_timestamp, :timestamp
t.column :mysql_timestamp, 'timestamp'
What you get is ar_timestamp being the usual DATETIME type where mysql_timestamp is defined as TIMESTAMP.
Here is the Customer:
class CreateCustomer < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.column :email, :string, :null => false
end
end
def self.down
drop_table :customers
end
end
And this is the customer Info:
class CustomerInfo < ActiveRecord::Migration
def self.up
create_table :statuses do |t|
t.column :statuses, :string, :null => false
end
end
def self.down
drop_table :status
end
end
What I would like to do is the customer and customer Info have a one to one relationship. How can I do it in a new migration? thank you.
When you want a 1 to 1 in Rails, you have to decide which one of the models will store the foreign key. In your case, you probably want status to store the fk, so add an integer column called customer_id to the status table. Then you can add the has_one/belongs_to on Customer and Status. belongs_to always goes on the model with the foreign key.
Also I'm not sure if Rails will like you calling your table with the singular name, so you will probably have to do some extra work if you really want to call it 'status' instead of 'statuses'
You can try following thing in your next migration
add_column :customer_infos , :customer_id , :integer ,:references=>"customers" , :null=>:true
Then you can add the has_one/belongs_to on Customer and Cusomer_infos .
You can also execute an SQL statement.
statement = "ALTER TABLE users CHANGE id id SMALLINT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT" ActiveRecord::Base.connection.execute(statement)
you can entry manually in your migration
Note this is just an example. The final SQL statement syntax depends on the database.
Consider the table creation script below:
create_table :foo do |t|
t.datetime :starts_at, :null => false
end
Is it's possible to set the default value as the current time?
I am trying to find a DB independent equivalent in rails for the SQL column definitions given below:
Oracle Syntax
start_at DATE DEFAULT SYSDATE()
MySQL Syntax
start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
OR
start_at DATETIME DEFAULT NOW()
This is supported now in Rails 5.
Here is a sample migration:
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
See discussion at https://github.com/rails/rails/issues/27077 and answer there by prathamesh-sonpatki
You can add a function in a model like this:
before_create :set_foo_to_now
def set_foo_to_now
self.foo = Time.now
end
So that the model will set the current time in the model.
You can also place some sql code in the migration for setting the default value at the database level, something like:
execute 'alter table foo alter column starts_at set default now()'
Setting something like this:
create_table :foo do |t|
t.datetime :starts_at, :null => false, :default => Time.now
end
causes executing the Time.now function during migrating so then the table in database is created like this:
create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');
but I think that it is not what you want.
If you need to change an existing DateTime column in Rails 5 (rather than creating a new table as specified in other answers) so that it can take advantage of the default date capability, you can create a migration like this:
class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
def change
change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
end
end
Active Record automatically timestamps create and update operations if the table has fields named created_at/created_on or updated_at/updated_on. Source - api.rubyonrails.org
You don't need to do anything else except to have that column.
I was searching for a similar solutions but I ended using https://github.com/FooBarWidget/default_value_for.
The default_value_for plugin allows one to define default values for ActiveRecord models in a declarative manner. For example:
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
I usually do:
def change
execute("
ALTER TABLE your_table
ALTER COLUMN your_column
SET DEFAULT CURRENT_TIMESTAMP
")
end
So, your schema.rb is going to have something like:
create_table "your_table", force: :cascade do |t|
t.datetime "your_column", default: "now()"
end
Did you know that upserts fail unless you have a default updated_at/created_at????
there is no migration flag which automatically does this, you have to manually include an options object with a default key
create_table :table_foos do |t|
#...
# date with timestamp
t.datetime :last_something_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
# standard timestamps
t.timestamps({default: -> { "CURRENT_TIMESTAMP" }})
end
In the answer given by #szymon-lipiński (Szymon Lipiński), the execute method didn't work for me. It was throwing a MySQL syntax error.
The MySQL syntax which worked for me is this.
execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
So to set the default value for a datetime column in migration script can be done as follows:
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