I'm not sure I'm applying the Rails change migration properly - ruby-on-rails

I'm building a Rails application that allows a user to catalogue tires by inputting values into the UI. When I created the tire class , I made it as
class CreateArticles < ActiveRecord::Migration[5.0]
def change
create_table :tires do |t|
t.decimal :price
...
end
end
When I ran the program I realized that I'd neglected to specify the precision and scale of the decimal attribute. This resulted in the program being unable to accept non-integer values and displaying all values appended by .0 Because I was so much farther along on the program, I decided to write another migration to just change price to a float by writing the following migration:
class ChangeTiresToFloat < ActiveRecord::Migration[5.0]
def change
change_column :tires, :price, :float
end
After running db:migrate, there is no change in the behavior of the program. Should this not have made it so that the UI could accept float values?
Edit: Before resorting to making the price column a float value, I did try to fix the decimal value by adding the missing attributes with this migration:
class MoneyDecimalFix < ActiveRecord::Migration[5.0]
def change
change_column :tires, :price, :decimal, :precision => 8, :scale => 2
end
end
After rolling forward the migration, the program's behavior still did not change.

Although it is possible to use a change_column migration to edit the price value to include the decimal attribute's specifications, because the create_table is a reversible migration, it is advisable to instead roll the migration back and manually implement the change to ultimately read
t.decimal :price, :precision => 8, :scale => 2
then roll the migration forward again.
It is however worth noting that, although having the price column correctly defined in the migration is an essential step, it alone did not fix the issue of the form not accepting decimal values.
The error was ultimately in my Views. Within Rails, the number_field tag, on its own, will not accept decimal values, it must be 'combined' with a text_field tag in order to do so as such:
<%= f.number_field :price, class: :text_field, step: :any %>

Related

How to change already added string column to a Reference in Rails

I've already created a model in Rails to collect some user information
I created the columns as :string initially but I've since changed the way this data is looked up and entered by using separate populated models.
Now instead of entering into these fields as string - i want these columns to be "references" instead.
Is there an easy way to change from the string to reference without having to create a new model entirely?
*do not need to save the existing data
Is there any data in the strings you would like to save?
Or is it just because it has the same name?
You don't have to create a new model.
You could create a simple migration
remove_column :table, :your_column_name, :string
add_column :table, :your_column_name, :integer, references: :your_parent_model
You can add a temporary string column to save the string column first:
rails g migration add_temporary_string_column_to_model temporary_string_column:string
And run rails console:
SomeModel.all.each do |some_model|
some_mode.temporary_string_column = some_mode.string_column
some_mode.save
end
And now you can change your original string column's type to references which is an int(4) column in MySQL, migration like this:
class ChangeFormatInSomeTable < ActiveRecord::Migration
def change
change_column :some_table, :string_column, :references
end
end
Finally, you can run rails console again to convert the string data to integer like this:
SomeModel.all.each do |some_model|
some_mode.string_column = some_mode.temporary_string_column.to_i
some_mode.save
end
And at last, remove the temporary string column:
rails g migration remove_temporary_string_column_from_model temporary_string_column
Here is another solution, without dropping the column itself (not exactly in my case). I'm not sure though if this is the best solution.
In my case, I have a tickets table that holds purchase_uid in itself. I decided to keep purchases in another table after making the necessary improvements in our backend. Purchases table has uuid as the primary key. Given this background, here is my migration to change my column into a reference.
class AddPurchaseRelationToTickets < ActiveRecord::Migration[5.2]
def up
change_column :tickets, :purchase_uid, :uuid, references: :purchase, foreign_key: true, using: 'purchase_uid::uuid'
end
def down
change_column :tickets, :purchase_uid, :string
end
end
In my case, since string doesn't automatically cast into uuid, purchase_uid were dropped and recreated as well. However, if you decide to keep the column type same, I don't think it will be a problem.
You can create migrations to serve the exact purpose.
rails generate migration AddAddressToUsers address:references
This will create a migration file in db/migrate directory.
Then run: rails db:migrate to run migration and make changes in your database.
Don't forget to create associations in your models (belongs_to, has_many, etc.) depending on your system structure.
Wanted to add a simpler alternative to the accepted answer that preserves data:
class ChangeStringToInt < ActiveRecord::Migration[5.1]
def up
change_column :table_name, :field_name, :integer, null: false, references: :table_referenced, using: 'field_name::integer'
add_index :chapter_actions, :field_name
end
def down
change_column :table_name, :field_name, :string, null: false, using: 'field_name::character varying'
remove_index :table_name, :field_name
end
end

method names in Rails migrations

I'm using Agile Web Development with Rails to learn about Rails. In an early chapter, the author created scaffolding and then started looking at the Migration. In his Migration, there is an "up" and and "down" method, whereas I only have a "change" method in my Migration. The author is using Rails 3.05 (or something like that) and I am using 3.1, however, I don't think that's the explanation, because using another book but same version of Rails I remember creating a migration that had the "up" and "down" methods...
So, two questions,
a) What's the reason why I have different method names in my migration?
b) is it going to affect functionality?
My Migration
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string : title
t.text :description
t.string :image_url
t.decimal :price, :precision => 8, :scale => 2
t.timestamps
end
end
end
Books Migration
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.string :title
t.text :description
t.string :image_url
t.decimal :price, :precision => 8, :scale => 2
t.timestamps
end
end
def self.down
drop_table :products
end
end
Rails 3.1 did away with both the "up" and "down" part of migrations. Now they are called "reversible migrations" that use the change method. So your first code example is correct for Rails 3.1, second is correct for 3.0.x and earlier. Here are the change notes for 3.1 that skim over this update:
https://gist.github.com/958283
The important line: Migration files generated from model and constructive migration generators (for example, add_name_to_users) use the reversible migration's change method instead of the ordinary up and down methods.
The update makes sense if you think about it... you no longer have to define all the steps to "up" your database as well as type out the same steps in reverse to "down" your database. The change method is smart enough to go back and forth given the singe set of instructions.
To answer your second question, no it won't change how the migration works. It will still update your data store per your instructions, keep track of the migration, etc. It's just a more efficient way of describing those changes to your Model.
Same code really, just more dry (and slightly less customizeable).
Theres a good description here:
http://edgerails.info/articles/what-s-new-in-edge-rails/2011/05/06/reversible-migrations/index.html

rails date_select

Right now I am saving my dates as a string in the format of mm/dd/yyyy, but want to convert to date_select but I keep getting errors for some reason.
Here is the code that I am using
the form
<%= f.date_select :start_date %>
the model
validates :start_date, :presence => true
but I get an error from my controller saying that it doesnt fit the params.
That's because of the way Rails automatically looks at a database column to figure out what type of object is going to be stored there. In this case, Rails is looking for a Datetime column to be used in conjunction with the date_select helper, but instead it's finding a varchar column.
I would run a migration to drop the start_date column, and re-add it as a datetime column, like so
To generate a new migration:
rails generate migration [name of your migration]
In your case something like:
rails generate migration change_start_date_column_to_timestamp
This will generate a file in your RAILS_ROOT/db/migrations folder, which will look something like:
class ChangeStartDateColumnToTimestamp < ActiveRecord::Migration
def self.up
end
def self.down
end
end
And you need to modify it to look like:
class ChangeStartDateColumnToTimestamp < ActiveRecord::Migration
def self.up
remove_column :table_name, :start_date
add_column :table_name, :start_date, :timestamp
end
def self.down
remove_column :table_name, :start_date
add_column :table_name, :start_date, :string
end
end
Then, when rails pulls the data from the database, it'll automatically convert them to Ruby Time objects.
A word of caution... this will destroy the data in the start_date field. So if you have pre-existing information that needs to be preserved, you need to do something more complicated.

Adding a Model Reference to existing Rails model

I'd like to know the "proper" way to approach adding a relation between two existing classes in Rails 3.
Given existing models: Clown & Rabbit
I'd like to add a reference (belongs_to) from Rabbit to Clown. I start by trying to generate a migration:
rails g migration AddClownToRabbits clown:reference
which gives me a migration that looks like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown, :reference
end
def self.down
remove_column :rabbits, :clown
end
end
After rake db:migrate on this migration I examine SQLite3's development.db and see a new column: "clown" reference
I guess I was expecting a "clown_id" integer column and a migration that looked like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown_id
end
def self.down
remove_column :rabbits, :clown_id
end
end
I'm sure :reference is supposed to be equivalent to "t.references :clown" but I can't find the documentation (big surprise). API says add_column: Instantiates a new column for the table. The type parameter is normally one of the migrations native types, which is one of the following: :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean.
...with no reference to :reference.
If you are using edge rails (4.0) you can use:
rails generate migration AddAddressRefToContacts address:references
As you can see by the docs.
After you set belongs_to in Rabbit, and has_many in Clown, you can do a migration with:
add_column :rabbit, :clown_id, :integer
EDIT: See Paulo's answer below for a more updated answer (Rails 4+)
I'm not sure where you got this idea, but there is no (and never has been) such syntax to do what you want with add_column. To get the behavior you want, you'd have to do t.refences :clown, as you stated. In the background this will call: #base.add_column(#table_name, "#{col}_id", :integer, options).
See here.
EDIT:
I think I can see the source of your confusion. You saw the method call t.reference and assumed it was a datatype because calls such as t.integer and t.string exist, and those are datatypes. That's wrong. Reference isn't a datatype, it's just simply the name of a method, similar to t.rename is.

varchar Migration question for Ruby on Rails

I have created a new table including a column "note". The default is varchar(255) I believe but I wish to have this column be a text area vs. a field and to allow more data. I imagine that I would make this change in ActiveRecord::Migration file but I am curious as to the format. Do I simply change the varchar(255) to varchar(1000) for example? (if so what is the format?
def self.up
create_table :notes do |t|
t.string :note :varchar(1000)
end
Is that the right format? Furthermore, how do I get the entry field to be multiple rows. Sorry if this is easy stuff but I am new to programming and RoR. Thanks.
The correct format would be
t.string :note, :limit => 1000
make sure you are using a version of MySQL(or whichever database) which supports varchars longer than 256 characters.
if you want to use a large text block it would be
t.text :note
See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html for more information
You can change the length with the limit option as so...
def self.up
change_column :notes, :note, :string, :limit => 1000
end
You can simply use the 'text' type instead of 'string'.
def self.up
create_table :notes do |t|
t.text :note
end
end
Using the 'text' type will result in database column of type TEXT. Varchar is usually limited to a maximum length of 255 (in MySQL, other RDBMSs have similar limits).
If you use Rails' form helpers, a textarea will be output for this field (because it is of type 'text'). textarea is the form element that accepts multi-line input.
Edit: If you've already migrated the create_table, you can create a new migration to change the column type:
def self.up
change_column :notes, :note, :text
end
Since I had a lot of data already stored I used
self.up
change_column :notes, :note, :text, :limit => nil
end
If I left off the :limit => nil option then the column type would change from varchar to text, but it still had a max length of 255 characters.

Resources