带有has_many关系的Rails hstore和accepts_nested_attributes_for不存储嵌套对象

时间:2015-02-13 22:17:19

标签: ruby-on-rails-4 nested-attributes has-many rails-postgresql hstore

Rails不希望存储已更改的嵌套对象属性。我用:

  • Rails 4
  • Postgresql 9.3
  • Postgresql中的Hstore列
  • 视图中的嵌套属性
  • 使用rolify gem

这里是数据库表:

create_table :accounts_roles do |t|
  t.references :account, null: false
  t.references :role,    null: false
  t.hstore     :configurations
  t.timestamps
end

这里的模型:

class Account < ActiveRecord::Base
  has_many :accounts_roles
  has_many :roles, through: :accounts_roles

  accepts_nested_attributes_for :accounts_roles
end

class AccountsRole < ActiveRecord::Base
  belongs_to :account
  belongs_to :role

  store_accessor :configurations, :color
end

控制器允许调试的所有属性:

def account_params
  params.require(:account).permit!
end

在我看来,我使用fields_for:

<%= form_for @account ... %>
  ...
  <%= f.fields_for :accounts_roles do |ar| %>
    <%= ar.text_field :color ... %>

如果我提交编辑表单,参数hash看起来很好:

{"utf8"=>"✓", "account"=>{..., "accounts_roles_attributes"=>{"0"=>{"color"=>"green", "id"=>"5"}}},...}

但颜色不会变为“绿色”,它仍然是“红色”!

在rails控制台中,我尝试手动执行相同的过程:

> u = Account.first
> u.account_roles.first.color
 => "red"
> u.update(accounts_roles_attributes: { color: "green", id: 5 } )
 => true
> u.account_roles.first.color
 => "green"
> u.save
 => true
> u = Account.first
> u.account_roles.first.color
 => "red"

但没有成功。属性颜色保持“红色”。 有什么想法吗?

1 个答案:

答案 0 :(得分:0)

解决了问题!我在模型账户上使用rolify gem。我已决定将定义的rolify关联has_and_belongs_to_many :roles更改为has_many :accounts_roleshas_many :roles, through: :accounts_roles。这引发了问题,因为gem定义了gem内部的关联。因此,该模型定义了两个关联,has_and_belongs_to_many :roleshas_many through。钩子解决了问题(从模型帐户中删除关联has_many :accounts_roleshas_many :roles, through: :accounts_roles)。

# config/initializers/hook_rolify.rb
module Rolify
  def rolify(options = {})
    include Role
    extend Dynamic if Rolify.dynamic_shortcuts

    options.reverse_merge!({:role_cname => 'Role'})
    self.role_cname = options[:role_cname]
    self.role_table_name = self.role_cname.tableize.gsub(/\//, "_")

    default_join_table = "#{self.to_s.tableize.gsub(/\//, "_")}_#{self.role_table_name}"
    options.reverse_merge!({:role_join_table_name => default_join_table})
    self.role_join_table_name = options[:role_join_table_name]

    rolify_options = { :class_name => options[:role_cname].camelize }
    # NOTE not needed any more
    # rolify_options.merge!({ :join_table => self.role_join_table_name }) if Rolify.orm == "active_record"
    rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove ].include? k.to_sym })

    # NOTE defining the association has_many through
    # has_and_belongs_to_many :roles, rolify_options
    has_many self.role_join_table_name.to_sym
    has_many :roles, rolify_options.merge!(through: self.role_join_table_name.to_sym)

    self.adapter = Rolify::Adapter::Base.create("role_adapter",  self.role_cname, self.name)
    load_dynamic_methods if Rolify.dynamic_shortcuts
  end
end