multiparameter error with datetime_select

后端 未结 6 732
傲寒
傲寒 2021-01-02 09:13

I have the following code in my form.

<%= f.datetime_select(:date_time, :prompt => {:day => \'Day\', :month => \'Month\', :year => \'Year\'},          


        
相关标签:
6条回答
  • 2021-01-02 09:39

    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))
    
    0 讨论(0)
  • 2021-01-02 09:40

    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
    
    0 讨论(0)
  • 2021-01-02 09:46

    I was facing the same problem. I just added attr_accessible for that attribute and it works fine.

    Hope it helps.

    0 讨论(0)
  • 2021-01-02 09:47

    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....

    0 讨论(0)
  • 2021-01-02 09:48

    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.

    0 讨论(0)
  • 2021-01-02 10:02

    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

    0 讨论(0)
提交回复
热议问题