How to store an array of numbers in database using rails - ruby-on-rails

I am pretty new to rails so this may be a dumb question, but I have spent hours debugging this seemingly simple error and gotten nowhere. In my database, I want each row to to store an array of numbers (in a variable called contributions). I read that the best way to do that was by adding the following code:
class Goal < ActiveRecord::Base
#...
serialize :contributions, Array
#...
end
The attribute is also in goal.rb
ActiveRecord::Schema.define(version: 20150711171452) do
create_table "goals", force: :cascade do |t|
#...
t.decimal "contributions"
#...
end
end
I added it via this migration:
class AddContributorsToGoals < ActiveRecord::Migration
def change
add_column :goals, :contributors, :string
add_column :goals, :contributions, :decimal
end
end
The only problem is that whenever I try to access this attribute, I always get errors stating that it is of type BigDecimal. Rails thinks that this is a single number, but I want it to be a list of numbers. Any ideas as to what I could be doing wrong?
One example of a problem I get is that when this executes:
#goal.contributions << #goal.lastUpdateAmount
but I get this error
undefined method `<<' for #<BigDecimal:7f95e082ab18,'-0.0',9(9)>
I followed identical steps with an array of strings and that was serialized properly. I just don't know what's going on with this one.

Please change data type decimal to text
class AddContributorsToGoals < ActiveRecord::Migration
def change
add_column :goals, :contributions, :text
end
end

Related

Rails: Adding migration to add an array (default empty)

I'm trying to add a column called share to one of my resources.
The idea is that users can upload documents and share them with other (specific) users, and the array contains the emails of those that the user wants to share with.
I tried adding a migration with the code
class AddShareToDocuments < ActiveRecord::Migration
def change
add_column :documents, :share, :array, :default => []
end
end
But when I open up rails console in the command prompt, it says that share:nil and user.document.share.class is NilClass.
Creating a new array in the rails console sandbox by typing
newarray = []
says that newarray.class is Array.
Can anyone spot what I'm doing wrong?
Rails 4 the PostgreSQL Array data type
In terminal
$ rails generate migration AddTagsToProduct tags:string
Migration file:
class AddTagsToProduct < ActiveRecord::Migration
def change
add_column :products, :tags, :string, array: true, default: []
end
end
https://coderwall.com/p/sud9ja/rails-4-the-postgresql-array-data-type
if you want support all databases you must serialize the array in a String
class Documents < ActiveRecord::Base
serialize :share
end
class AddShareToDocuments < ActiveRecord::Migration
def change
add_column :documents, :share, :string, :default => []
end
end
In case of Postgresql and array datatype I found https://coderwall.com/p/sud9ja
Arrays are not normally a type to be stored in a database. As michelemina points out, you can serialize them into a string and store them, if the type of the data in the array is simple (strings, int, etc). For your case of emails, you could do this.
If, on the other hand, you want to be able to find all of the User objects that a document was shared with, there are better ways of doing this. You will want a "join table". In your case, the join-table object may be called a Share, and have the following attributes:
class Share
belongs_to :user
belongs_to :document
end
Then, in your Document class,
has_many :shares
has_many :users, :through => :shares
As far as generating the migration, this may be hacky, but you could create a new migration that changes the type to "string" (Edit: correct code):
class AddShareToDocuments < ActiveRecord::Migration
def up
change_column :documents, :share, :string
end
def down
change_column :documents, :share, :array, :default => []
end
end

set_primary_key error in Rails

I'm creating a table called Index. Here's the migration:
class CreateIndices < ActiveRecord::Migration
def change
create_table :indices, {:id => false} do |t|
t.string :name
t.float :returns, array: true, default: []
t.float :navs, array: true, default: []
t.float :sharpe
t.timestamps
end
execute "ALTER TABLE indices ADD PRIMARY KEY (name);"
end
end
That all works fine. I saw in another Stack Overflow question that I have to include the set_primary_key command in my model to get it to work, so I have the following in the index.rb
class Index < ActiveRecord::Base
set_primary_key :name
end
Besides these two files, I haven't changed anything from the default Rails scaffolding (the app was created with Postgres as the default database). When I go to localhost:3000/indices, I get the following error
undefined method `set_primary_key' for #<Class:0x37132e0>
If I comment out the set_primary_key line it loads the regular empty scaffold, but I assume this does not give me the primary key functionality that I want. What am I doing wrong?
If you're using rails 3.2 or higher, the set_primary_key method was depreciated, check out the Rails 3.2 release notes - Section 8.1 and it suggests using an assignment method instead like self.primary_key=, just like you said you did in your comment
Exactly it's currently a class method :
http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/PrimaryKey/ClassMethods.html#method-i-primary_key-3D
Don't forget the adequate migration :
def up
create_table :books, {id: false, primary_key: :key_id} do |t|
t.timestamps
etc.
end
execute <<-SQL
ALTER TABLE books
ADD PRIMARY KEY (key_id)
SQL
end
def down
drop_table :books
end
If you want to use this new primary key in your generated routes (routes.rb) :
resources :books, only: [:show, :edit, :update], param: :key_id
Hope it helps !
Take care
Weird. As per documentation I would have expected
class Index < ActiveRecord::Base
set_primary_key "name"
end
to work. The only difference I can see is "name" string vs your :name symbol which shouldn't make any difference.
Update
Actually, bang up the top of that link is a deprecated message (sorry). Apparently it's now a class method so that explains the need for self.set_primary_key. Doh!

Undefined add_column in Rails 3

I am trying to run a migration in Rails 3, I wish to add a column to a table, the code looks like this:
class AddConstAdr < ActiveRecord::Migration
def change
change_table: constants do |t|
t.add_column :home_address, :string
end
end
end
When I do rake db:migrate I get an error saying undefined method 'add_column'. I am confused as to why this is happening, can anyone help?
You seem to be mixing two different ways of doing a migration. You probably want this:
def change
change_table :constants do |t|
t.string :home_address
end
end
or this:
def change
add_column :constants, :home_address, :string
end
Both forms should do the same thing: add a home_address string column to the constants table.
I'm also assuming that your change_table: constants is just a typo that should have been change_table :constants.
Further information may be found in the Migrations Guide.
You should do as below:
def change
add_column :constants, :home_address, :string
end

How do you deal with breaking changes in a Rails migration?

Let's say I'm starting out with this model:
class Location < ActiveRecord::Base
attr_accessible :company_name, :location_name
end
Now I want to refactor one of the values into an associated model.
class CreateCompanies < ActiveRecord::Migration
def self.up
create_table :companies do |t|
t.string :name, :null => false
t.timestamps
end
add_column :locations, :company_id, :integer, :null => false
end
def self.down
drop_table :companies
remove_column :locations, :company_id
end
end
class Location < ActiveRecord::Base
attr_accessible :location_name
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :locations
end
This all works fine during development, since I'm doing everything a step at a time; but if I try deploying this to my staging environment, I run into trouble.
The problem is that since my code has already changed to reflect the migration, it causes the environment to crash when it attempts to run the migration.
Has anyone else dealt with this problem? Am I resigned to splitting my deployment up into multiple steps?
UPDATE It appears I am mistaken; when migrating a coworker's environment we ran into trouble, but staging updated without issue. Mea Culpa. I'll mark #noodl's reply as the answer to bury this, his post is good advice anyway.
I think the solution here is to not write migrations which have any external dependencies. Your migrations should not depend on the past or current state of your model in order to execute.
That doesn't mean that you can't use your model objects, just that you shouldn't use the versions found in whatever version of your code happens to be installed when you run a particular migration.
Instead consider redefining your model objects within your migration file. In most cases I find that an empty model class extending ActiveRecord::Base or a very stripped down version of the model class I was using at the time I wrote the migration allows me to write a reliable, future proof, migration without needing to convert ruby logic into SQL.
#20110111193815_stop_writing_fragile_migrations.rb
class StopWritingFragileMigrations < ActiveRecord::Migration
class ModelInNeedOfMigrating < ActiveRecord::Base
def matches_business_rule?
#logic copied from model when I created the migration
end
end
def self.up
add_column :model_in_need_of_migrating, :fancy_flag, :boolean, :default => false
#do some transform which would be difficult for me to do in SQL
ModelInNeedOfMigrating.all.each do |model|
model.update_attributes! :fancy_flag => true if model.created_at.cwday == 1 && model.matches_business_rule?
#...
end
end
def self.down
#undo that transformation as necessary
#...
end
end
What error are you getting when you run the migrations? You should be fine as long as your rake files and migrations don't use your models (and they shouldn't).
You'll also want to switch the order of your drop_table and remove_column lines in the self.down of your migration.

What does this rake db:seed error mean?

I've been trying to solve this problem for a couple of hours but I can't seem to understand what's going on.
I'm using Rails 3 beta, and want to seed some data to the database. However, when I try to seed some values through db:seed, I get this error:
rake aborted!
Attribute(#81402440) expected, got Array(#69024170)
The seeds.rb is:
DataType.delete_all
DataType.create(
:name => 'String'
)
And I got these classes:
class DataType < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :data_types
end
Just to clarify, the intention is having Attribute objects have one data type (such as String, Number, etc.).
While the migration definition for DataType is merely:
class CreateDataTypes < ActiveRecord::Migration
def self.up
create_table :data_types do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :data_types
end
end
Can anyone tell me what I'm doing wrong?
"Attribute" may be conflicting with something. Try renaming your Attribute model.

Resources