2

我有一个过滤器类,其中包括 ActiveModel 并由两个日期组成:

class MealFilter
  include ActiveModel::Model

  attribute :day_from, Date
  attribute :day_to, Date
end

该模型呈现为如下形式:

  <%= form_for(@filter) do |f| %>
      <div class="form-group form-group--date">
        <%= f.label :day_from %>
        <%= f.date_select :day_from %>
      </div>
      <div class="form-group form-group--date">
        <%= f.label :day_to %>
        <%= f.date_select :day_to %>
      </div>
  <% end %>

现在的问题是,当表单被提交时,它将这个参数发送到控制器:

{"utf8"=>"✓", "meal_filter"=>{"day_from(1i)"=>"2016", "day_from(2i)"=>"1", "day_from(3i)"=>"29", "day_to(1i)"=>"2016", "day_to(2i)"=>"1", "day_to(3i)"=>"30"}, "commit"=>"Filter"}

我通过控制器参数提取值:

def meal_filter_params
  if params.has_key? :meal_filter
    params.require(:meal_filter).permit(:day_from, :day_to)
  end
end

如果我现在将 分配params[:meal_filter]给我的MealFilter班级@filter = MealFilter.new(meal_filter_params),我的日期字段不会正确更新。它接缝1i、2i、3i部分未正确分配给日期。

但是,如果使用ActiveRecord类,这可以正常工作。

我错过了一些包括吗?有谁知道,如果不在,这个魔法映射在哪里实现ActiveModel::Model

4

3 回答 3

2

我在将应用程序从 Rails 3.2 升级到 5.0 时遇到了这个问题

我如何整理如下:

对于 Rails 5

  class MealFilter
    include ActiveRecord::AttributeAssignment

    attr_reader :day_from

    def initialize(attributes = {})
      self.attributes = attributes || {}
    end

    def day_from=(value)
      @day_from = ActiveRecord::Type::Date.new.cast(value)
    end
  end

对于 Rails 4.2

  class MealFilter
    include ActiveRecord::AttributeAssignment

    attr_accessor :day_from

    def initialize(attributes = {})
      self.attributes = attributes || {}
    end

    def type_for_attribute(name)
      case name
      when "day_from" then ActiveRecord::Type::Date.new
      end
    end
  end

然后你可以这样做:

  attributes  = { "day_from(3i)" => "1", "day_from(2i)" => "9", "day_from(1i)" => "2020" }
  meal_filter = MealFilter.new(attributes)
  meal_filter.day_from
  # => Tue, 01 Sep 2020
于 2020-09-02T11:46:56.950 回答
0

您可以简单地从 date_select 帮助器中访问 1i、2i 和 3i 参数,并将它们组合起来以在 before_validation 回调中创建新的 Date:

class DateOfBirth
  include ActiveModel::Model
  include ActiveModel::Attributes
  include ActiveModel::Validations::Callbacks

  attribute :date_of_birth, :date
  attribute "date_of_birth(3i)", :string
  attribute "date_of_birth(2i)", :string
  attribute "date_of_birth(1i)", :string

  before_validation :make_a_date

  validates :date_of_birth, presence: { message: "You need to enter a valid date of birth" }

  def make_a_date
    year = self.send("date_of_birth(1i)").to_i 
    month = self.send("date_of_birth(2i)").to_i
    day = self.send("date_of_birth(3i)").to_i

    begin # catch invalid dates, e.g. 31 Feb
      self.date_of_birth = Date.new(year, month, day)
    rescue ArgumentError
      return
    end
  end
end
于 2020-06-09T18:43:14.597 回答
0

好的,找到了解决方案。我需要的是 MultiparameterAssignment,它实际上是在 ActiveRecord 中实现的,而不是在 ActiveModel 中。

据我所知,有一个开放的拉取请求(https://github.com/rails/rails/pull/8189)应该可以解决这个问题。

但与此同时,一些聪明人写了一个可以包含在模型中的模块:https ://gist.github.com/mhuggins/6c3d343fd800cf88f28e

您需要做的就是包含关注点并定义一个class_for_attribute方法,该方法返回您的属性应该映射到的类——在我的例子Date中。

于 2016-01-29T15:18:29.853 回答