加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 处理与虚拟属性对应的多参数属性的正确方法

发布时间:2020-12-17 02:04:55 所属栏目:百科 来源:网络整理
导读:我有一个Rails应用程序,其中包含一个包含birthdate属性的模型.这对应于使用ActiveRecord日期类型定义的数据库中的列.有了这个,我可以使用date_select表单助手方法在我的视图中将其渲染为三选输入.然后将对应于该字段的表单参数序列化回控制器作为生日(1i),生
我有一个Rails应用程序,其中包含一个包含birthdate属性的模型.这对应于使用ActiveRecord日期类型定义的数据库中的列.有了这个,我可以使用date_select表单助手方法在我的视图中将其渲染为三选输入.然后将对应于该字段的表单参数序列化回控制器作为生日(1i),生日(2i)和生日(3i).因此,我可以在我的模型上的控制器中使用标准的update_attributes方法来更新模型上的所有字段.

我现在正在尝试使用attr_encrypted gem加密这个字段.虽然gem支持编组(这很好),但不再存在类型为date的名称birthdate的真实列 – 而是attr_encrypted将值公开为由真实encrypted_birthdate列支持的虚拟属性birthdate.这意味着update_attributes无法执行先前的多参数属性分配来填充和保存此列.相反,我得到一个MultiparameterAssignmentErrors错误,该错误源于对内部column_for_attribute方法的调用,该方法为此列返回nil(来自execute_callstack_for_multiparameter_attributes中的某个位置).

我目前正在解决这个问题如下:

我在app / models / person.rb中的模型:

class Person < ActiveRecord::Base
  attr_encrypted :birthdate
end

我在app / controllers / people_controller.rb中的控制器:

class PeopleController < ApplicationController
  def update

    # This is the bit I would like to avoid having to do.
    params[:person] = munge_params(params[:person])

    respond_to do |format|
      if @person.update_attributes(params[:person])
        format.html { redirect_to @person,notice: 'Person was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @person.errors,status: :unprocessable_entity }
      end
    end
  end

  private

  def munge_params(params)
    # This separates the "birthdate" parameters from the other parameters in the request.
    birthdate_params,munged_params = extract_multiparameter_attribute(params,:birthdate)

    # Now we place a scalar "birthdate" where the multiparameter attribute used to be.
    munged_params['birthdate'] = Date.new(
      birthdate_params[1],birthdate_params[2],birthdate_params[3]
    )

    munged_params
  end

  def extract_multiparameter_attribute(params,name)
    # This is sample code for demonstration purposes only and currently
    # only handles the integer "i" type.
    regex = /^#{Regexp.quote(name.to_s)}((d+)i))$/
    attribute_params,other_params = params.segment { |k,v| k =~ regex }
    attribute_params2 = Hash[attribute_params.collect do |key,value|
      key =~ regex or raise RuntimeError.new("Invalid key "#{key}"")
      index = Integer($1)
      [index,Integer(value)]
    end]
    [attribute_params2,other_params]
  end

  def segment(hash,&discriminator)
    hash.to_a.partition(&discriminator).map do |a|
      a.each_with_object(Hash.new) { |e,h| h[e.first] = e.last }
    end
  end
end

我的观点app / views / people / _form.html.erb:

<%= form_for @person do |f| %>
    <%= f.label :birthdate %>
    <%= f.date_select :birthdate %>

    <% f.submit %>
<% end %>

处理这种类型的属性的正确方法是什么,而不必像这样引入params数组的临时替换?

更新:
看起来像this可能会引用相关问题.而且this也是.

另一个更新:

这是我目前的解决方案,基于Chris Heald的回答.此代码应添加到Person模型类中:

class EncryptedAttributeClassWrapper
  attr_reader :klass
  def initialize(klass); @klass = klass; end
end

# TODO: Modify attr_encrypted to take a :class option in order
# to populate this hash.
ENCRYPTED_ATTRIBUTE_CLASS_WRAPPERS = {
  :birthdate => EncryptedAttributeClassWrapper.new(Date)
}

def column_for_attribute(attribute)
  attribute_sym = attribute.to_sym
  if encrypted = self.class.encrypted_attributes[attribute_sym]
    column_info = ENCRYPTED_ATTRIBUTE_CLASS_WRAPPERS[attribute_sym]
    column_info ||= super encrypted[:attribute]
    column_info
  else
    super
  end
end

此解决方案按原样工作,但如果attr_encrypted采用将动态构造ENCRYPTED_ATTRIBUTE_CLASS_WRAPPERS哈希的:class选项,则会更好.我将看看我可以扩展/ monkeypatch attr_encrypted的方法来做到这一点.要点:https://gist.github.com/rcook/5992293.

解决方法

您可以对模型进行monkeypatch以通过column_for_attribute调用.我没有对此进行过测试,但它应该导致生日字段上的反射而不是返回encrypted_birthday字段的反射,这应该导致multiparam属性正确分配(因为AR将能够推断字段类型):

def column_for_attribute(attribute)
  if encrypted = encrypted_attributes[attribute.to_sym]
    super encrypted[:attribute]
  else
    super
  end
end

我们每this line修补一次column_for_attribute,以便AR可以推断出该列的正确类型.它需要弄清楚“birthday”的参数应该是DateTime类型的whatnot,并且不能从虚拟属性推断出.将反射映射到实际列应解决该问题.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读