I want to store multiple values (integers) in my database field. For that i use
serialize :subtype, Array
whenever i submit a form i get this parameters for subtype
"subtype"=>["",
"1",
"3"],
Now i try to save this values into my database. So i try to do something like that
#request.subtype << params[:subtype][1] << params[:subtype][2]
#request.save
Of course it doesn't work. How can i do that? How to store these values.
This is my migration:
create_table :requests do |t|
....
t.integer :subtype
....
t.timestamps null: false
end
In my form i have:
<%= f.grouped_collection_select :subtype,
RequestType.order(:typeName), :RequestSubTypes, :typeName, :id, :subTypeName,
{include_blank:false},{:class => "subTypes", multiple: true } %>
Serialize field can't be integer type.So change it to text type
Run this migration
rails g migration add_subtype_to_requests subtype:text
class Migration000000
def change
add_column :requests, :subtype, :text
end
end
Then this should work for you
#request.save
EDIT
In your controller allow subtype array
def request_params
params.require(:request).permit(:field1, :field2, :subtype => [])
end
You don't need to append to the column, just do this:
#request.subtype = params[:subtype].reject { |st| st.blank? }
#request.save
As sub_type is an array, this is all you need to do.
Related
I made an error and generated my Item model with string field instead of integer.
Here is my migration
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.string :name
t.string :url
t.text :photo
t.string :price
t.timestamps null: false
end
end
end
But right now I want to sort my items by price field and rails sorts it bad way because it is stored as string.
For example it thinks that price 9 is bigger than 1111111111.
Right now I order them like this:
#items=Item.where(:category_id => #active_category_id).order(:price)
What do I do?
Fix the column type. You can do it with the following migration:
class ChangePriceTypeInItems < ActiveRecord::Migration
def change
change_column :items, :price, :integer
end
end
The data in price column will be preserved if the string contained represents an integer value.
By the way, I think a price needs to be a decimal not an integer. But, you choose.
try this:
#items = Item.where(category_id: #active_category_id).sort { |a, b| b.price.to_i <=> a.price.to_i }
this gets all with your category id and then compares the prices as integers instead of strings. It should give you the order you're looking for.
I have over 100 recipes uploaded through ActiveAdmin (http://activeadmin.info/) in with the following attributes:
class CreateRecipes < ActiveRecord::Migration
def change
create_table :recipes do |t|
t.string :title
t.string :description
t.string :ingredients
t.string :position
t.timestamps
end
end
end
I needed to change position from a string to an integer. I was able to do this with the following:
change_column :table_name, :column_name, :integer
stackoverflow: Rails migration for change column
The issue is that I do not know how to go back and reassign all the recipes with a position (now that it's an integer). I basically want to start at 0 and go all the way to 100. And if i create a new recipe, it automatically has the position value of 101.
Is there a way to do this without going back and individually changing each recipe?
It sounds like you want to set :position to :id initially. You could do that through the rails console like so:
recipes = CreateRecipes.all
recipes.each do |recipe|
recipe.position = recipe.id
end
Then, for new recipes, in your model (create_recipes.rb), you could add:
after_initialize :default_values
...
def default_values
self.position ||= id
end
Incidentally, this is a nice clean way to handle default or initial values in general. For more information, see this excellent post How can I set default values in ActiveRecord?.
You can have the conversion run automatically as part of the migration itself. Add the code to convert the values in the existing records into the migration. Use self.up and self.down to have the appropriate conversion code for that direction of the migration:
class ChangeRecipePositionToInteger < ActiveRecord::Migration
def self.up
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :integer
position_values.each_pair do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_i
recipe.save
end
end
def self.down
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :string
position_values.each_pari do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_s
recipe.save
end
end
end
Can you teach me how to set an attribute to Array in Model?.
I have tried it but when I use array's method like push, each, I got the error undefined method `push' for nil:NilClass
My migrate look like this:
class CreateContacts < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :name
t.string :email
t.string :email_confirmation
t.integer :city_ids, array: true
t.text :description
t.timestamps
end
end
end
I would like to set attribute city_ids to Array.
One important thing to note when interacting with array (or other mutable values) on a model. ActiveRecord does not currently track "destructive", or in place changes. These include array pushing and poping, advance-ing DateTime objects.
Example
john = User.create(:first_name => 'John', :last_name => 'Doe',
:nicknames => ['Jack', 'Johnny'])
john = User.first
john.nicknames += ['Jackie boy']
# or
john.nicknames = john.nicknames.push('Jackie boy')
# Any time an attribute is set via `=`, ActiveRecord tracks the change
john.save
Referrence - link
You need to set a default. Otherwise, the attribute is nil, until you give it a value:
t.integer :city_ids, array: true, default: []
Or, you need to give it a value befor eyou attempt to use it:
c = City.find(...)
c.city_ids ||= []
c.city_ids.push(...)
I have the following migration:
Sequel.migration do
up do
create_table :user_settings do
primary_key :id
String :signature, null: true, text: true
end
alter_table :user_settings do
add_foreign_key :user_id, :users, null: false, on_delete: :cascade
add_index :user_id
end
end
down do
drop_table :user_settings
end
end
This will add default user settings.
The problem I have is that I want to create a row in the user_settings table for every user who is currently in the database that does not have a row, prior to this migration.
I want to check if each user has a row with a a matching user_id in the database and if not, I want to insert some default values.
How can I do this in a migration?
Normally, this kind of things are done using rake task but you need in the migration. I guess you have added the association in the User and UserSetting model and it will be has_one association. You need to create a new migration file
def up
users = User.includes([:user_setting]).where(:user_setting => {:user_id => nil})
users.each do |user|
user.create_user_setting
# OR you can write
# UserSetting.create({:user_id => user.id, :signature => 'your-custom-text'})
end
end
I ended up with this:
Sequel.migration do
up do
existing_settings = SequelAdapter::UserSettings.select(:user_id).to_a
SequelAdapter::User.exclude(id: existing_settings).each do |user|
SequelAdapter::UserSettings.create({user_id: user.id})
end
end
end
Thanks #bachan Smruty who pointed me in the right direction but there is no includes method.
I would like to create an enum field at sone migration I'm doing, I tried searching in google but I can't find the way to do it in the migration
the only thing I found was
t.column :status, :enum, :limit => [:accepted, :cancelled, :pending]
but looks like the above code runs only on rails 1.xxx and since I'm running rails 2.0
this what I tried but it fails
class CreatePayments < ActiveRecord::Migration
def self.up
create_table :payments do |t|
t.string :concept
t.integer :user_id
t.text :notes
t.enum :status, :limit => [:accepted, :cancelled, :pending]
t.timestamps
end
end
def self.down
drop_table :payments
end
end
So, in case that isn't allowed, what do you think could be a good solution? just a text field, and validating from the model?
You can manually specify the type by using the t.column method instead. Rails will interpret this as a string column, and you can simply add a validator to the model like Pavel suggested:
class CreatePayments < ActiveRecord::Migration
def self.up
create_table :payments do |t|
t.string :concept
t.integer :user_id
t.text :notes
t.column :status, "ENUM('accepted', 'cancelled', 'pending')"
t.timestamps
end
end
def self.down
drop_table :payments
end
end
class Payment < ActiveRecord::Base
validates_inclusion_of :status, :in => %w(accepted cancelled pending)
end
Look at tip #3 on http://zargony.com/2008/04/28/five-tips-for-developing-rails-applications
This exactly what you need!
class User < ActiveRecord::Base
validates_inclusion_of :status, :in => [:active, :inactive]
def status
read_attribute(:status).to_sym
end
def status= (value)
write_attribute(:status, value.to_s)
end
end
HTH
You can try the (very) comprehensive jeff's enumerated_attribute gem OR go with this simple workaround:
class Person < ActiveRecord::Base
SEX = [:male, :female]
def sex
SEX[read_attribute(:sex)]
end
def sex=(value)
write_attribute(:sex, SEX.index(value))
end
end
And then declare the sex attribute as an integer:
t.integer :sex
This worked very fine for me! =D
I have dozens of these little enums, with 3-300 entries in each. I implement them as lookup tables. I don't have a model file for each one; I use some metaprogramming to generate a model for each, since each table has the same set of columns (id, name, description).
Since some of the sets had enough elements to warrant their own table, it was more consistent to move them all to tables. Just another option if you'll have more of these enums later.
EDIT: Here's how I generate the models:
ACTIVE_RECORD_ENUMS = %w{
AccountState
ClientType
Country
# ...
}
ACTIVE_RECORD_ENUMS.each do |klass|
eval "class #{klass} < ActiveRecord::Base; end"
klass.constantize.class_eval do
class << self
def id_for(name)
ids[name.to_s.strip.humanize.downcase]
end
def value_for(id)
values[id.to_i]
end
def values
#values ||= find(:all).inject({}) {|h,m| h[m.send(primary_key)] = m.name; h}
end
def ids
#ids ||= self.values.inject({}) {|h, {k, v}| h[v.downcase] = k; h}
end
end
end
end
This file lives in the models directory, and is included in application_config.rb. This lets me do stuff like this:
AccountState.ids
# => {"active" => 1, "deleted" => 2}
AccountState.values
# => {1 => "Active", 2 => "Deleted"}
AccountState.id_for("Active")
# => 1
AccountState.value_for(1)
# => "active"
Have you looked at the enum-column plugin on RubyForge?
Similarly, the enumerated_attribute gem manages enums at the object level.
enum_attr :status, %w(accepted cancelled ^pending)
Define a string in the migration
t.string :status
Also provides some nice features like dynamic predicate methods.
http://github.com/jeffp/enumerated_attribute/tree/master
Add the following:
module ActiveRecord
module ConnectionAdapters #:nodoc:
class TableDefinition
def enum(*args)
options = args.extract_options!
column_names = args
column_names.each { |name| column(name, 'enum', options) }
end
end
end
end
to lib/enum/table_definition.rb and include it in your init.rb.
ok, just read the whole rails api and found what I neeed and I dont like :(
Rails doesn't support emum as native type on migrations, here is the info, I need to search for a plugin or other method.
I will keep you posted.
Another option: drop to SQL.
def self.up
execute "ALTER TABLE `payments` ADD `status` ENUM('accepted', 'cancelled', 'pending')"
end
With simple_form i use this:
<%= f.input :gender, :collection => {'Male' => 'male','Female' => 'female'}, :include_blank => false %>