I have the following code in my form.
<%= f.datetime_select(:date_time, :prompt => {:day => \'Day\', :month => \'Month\', :year => \'Year\'},
Christian. This is a bug in Rails that checks the database to infer the type needed for the multiparameter attributes. My guess is that your "date_time" attribute is not associated with a time column in your database.
I recently tackled this problem where I wanted a non-database attribute to accepted multiparameter attributes, this was the best solution I could come up with:
I found myself wanting to set an attr_accessor
to handle passing a date to my model in a form_for
tag with the f.datetime_select
helper. So this is what I had:
Model:
attr_accessor :my_time
View:
<%= f.datetime_select :my_time %>
Unfortunately when I submit my form I get this:
1 error(s) on assignment of multiparameter attributes
Well it turns out that this is actually a Rails bug a ticket for which has been submitted. In the meantime how do we make this work? The only solution I could find that was remotely attractive was to make use of composed_of
as a replacement for attr_accessor
. so...
Model:
composed_of :my_time,
:class_name => 'Time',
:mapping => %w(Time to_s),
:constructor => Proc.new{ |item| item },
:converter => Proc.new{ |item| item }
I know almost nothing about the composed_of
method so you should probably do your own reading on it, but what I do know is that it creates both a reader and writer for the given instance variable, and more importantly, the setter accepts multiparameter attributes. How I chose the options:
class_name: the name of our expected class. In this case, Time
mapping: the first argument is the class and the second argument seems to work with any method that an instance of the class responds to. I chose to_s
constructor: Not really sure how this is supposed to work. Seems to be called when @my_time
is nil
.
converter: Not really sure how this is supposed to work. Seems to be called when from my_time=, but doesn't seem to be applied with mass assignment.
One problem I ran into with this solution was that times were getting set in UTC instead of the environment's time zone. So unfortunately we cannot use my_time directly, but instead need to convert it to the proper time zone:
Time.zone.parse(my_time.to_s(:number))
What Does ActiveRecord::MultiparameterAssignmentErrors Mean?
def initialize(attributes={})
date_hack(attributes, "deliver_date")
super(attributes)
end
def date_hack(attributes, property)
keys, values = [], []
attributes.each_key {|k| keys << k if k =~ /#{property}/ }.sort
keys.each { |k| values << attributes[k]; attributes.delete(k); }
attributes[property] = values.join("-")
end
I was facing the same problem. I just added attr_accessible for that attribute and it works fine.
Hope it helps.
Remove :include_blank => false
from your code.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15 %>
Thanks....
I had the same problem using a date dropdown that wasn't backed by a database attribute. I wrote a little Rack middleware to cope with the problem:
class DateParamsParser
def initialize(app)
@app = app
end
def call(env)
if %w{POST PUT}.include? env['REQUEST_METHOD']
params = Rack::Utils.parse_query(env["rack.input"].read, "&")
# selects only relevant params like 'date1(1i)'
filtered_params = params.select{ |key, value| key =~ /\(\di\)/ }
# delete date params
filtered_params.each { |key, value| params.delete(key) }
# returns something like {'date1' => [2012, 5, 14], 'date2' => [2002, 3, 28]}
date_array_params = filtered_params.sort.reduce({}) do |array_params, keyvalue|
date_key = keyvalue.first.match(/(.+)\(/)[1] + ']'
array_params[date_key] ||= []
array_params[date_key] << keyvalue.last
array_params
end
# Creates params with date strings like {'date1' => '2012-5-14', 'date2' => '2002-3-28'}
date_params = Hash[date_array_params.map{ |key, date_array| [key, date_array.join('-')] }]
params.merge! date_params
env["rack.input"] = StringIO.new(Rack::Utils.build_query(params))
env["rack.input"].rewind
end
@app.call(env)
end
end
And in application.rb I put
config.middleware.insert_before ActionDispatch::ParamsParser, "DateParamsParser"
Note that I only build a date string here. So if you also require time you'll need to build the date_params
differently.
I faced the same problem with the model below
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
end
The corresponding form with the field for the expiration date:
<div class="field">
<%= f.label :expiration_date %>
<%= f.date_select(:expiration_date, start_year: Time.now.year + 3, :end_year => Time.now.year - 3, discard_day: true) %>
</div>
as mentioned by @gabeodess the problem is checking the database to infer the type accordingly the solution I did for it was adding the following code to the model to put the type of the needed attribute in this case :expiration_date so the model is modified to be the following
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
columns_hash["expiration_date"] = ActiveRecord::ConnectionAdapters::Column.new("expiration_date", nil, "date")
end
Hope this is useful