I have a model named UserPrice
which has the attribute :purchase_date
(a date_select) in its table. With my form I can create multiple user_prices at once but for user convenience I made a virtual attribute inside of my UserPrice
model called :all_dates
that's also a date_select field and its job is to be the replacement of the :purchase_dates
so users only have to select the :all_dates
field for the date.
Problem & Question
The :all_dates
field is not updating the :purchase_date
fields of my user_prices that are being created. What do I need to do in order to get my :all_dates
field to update the :purchase_date
fields of my new UserPrices
?
Does anyone have any tips on how to do this?
Parameters
Parameters:
"user_price"=> {
"all_dates(2i)"=>"10",
"all_dates(3i)"=>"27",
"all_dates(1i)"=>"2011"
},
"user_prices"=>
{
"0"=>{"product_name"=>"Item1", "store"=>"Apple Store","price"=>"6"},
"1"=>{"product_name"=>"Item2", "store"=>"Apple Store", "price"=>"7"}
},
"commit"=>"Submit"}
Code
class CreateUserPrices < ActiveRecord::Migration
def self.up
create_table :user_prices do |t|
t.decimal :price
t.integer :product_id
t.date :purchase_date
t.timestamps
end
end
end
I took out the :purchase_date
field so it isn't inside of the user_price loop.
<%= form_tag create_multiple_user_prices_path, :method => :post do %>
<%= date_select("user_price", "all_dates" ) %>
<% @user_prices.each_with_index do |user_price, index| %>
<%= fields_for "user_prices[#{index}]", user_price do |up| %>
<%= render "user_price_fields", :f => up %>
<% end %>
<% end %>
<% end %>
class UserPrice < ActiveRecord::Base
attr_accessible :price, :product_name, :purchase_date, :all_dates, :store
attr_accessor :all_dates
after_save :save_all_dates_to_user_prices
composed_of :all_dates, :class_name => "DateTime",
:mapping => %w(Time to_s),
:constructor => Proc.new { |item| item },
:converter => Proc.new { |item| item }
def user_prices
@user_prices = Array.new() { UserPrice.new }
end
protected
def save_all_dates_to_user_prices
if !self.all_dates.nil?
self.user_prices.each {|up| up.purchase_date = self.all_dates if up.new_record?}
end
end
class UserPricesController < ApplicationController
def new
@user_prices = Array.new(5) { UserPrice.new }
end
def create_multiple
@user_prices = params[:user_prices].values.collect { |up| UserPrice.new(up) }
if @user_prices.all?(&:valid?)
@user_prices.each(&:save!)
redirect_to :back, :notice => "Successfully added prices."
else
redirect_to :back, :notice => "Error, please try again."
end
end
This is a case of trying to do in a model what is better left to the controller. All you're trying to do here is to auto-assign a certain attribute on creation from a parameter not directly tied to your model. But you're not even passing that extra parameter to the model anywhere - you're creating your model instances from the
user_prices
parts of the parameter hash, but theuser_price
sub-hash is not used anywhere. In any case, this is behavior that is more closely related to the view and action taken than the model, so keep it in the controller.Try this:
after_save
callback stuffuser_prices
method in your modelall_dates
attribute name back topurchase_date
in the formThen your parameter hash should look like this:
All that's left to do is to merge the single
user_price
attributeS into eachuser_prices
sub-hash in yourcreate_multiple
action. Replace the first line in that action with this:I'm not sure why you are even using that virtual attribute is there more to this implementation? If you are just trying to save an associated model, you might simply want a
accepts_nested_attributes_for :user_prices
in your User modelThis works great and many developers use this method, so it's nice to know for working on other projects as well as for the people who might end up maintaining yours.
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2