has_and_belongs_to_many关系的自定义活动管理表单输入

Cri*_*ian 5 forms ruby-on-rails associations ruby-on-rails-3 activeadmin

我有一个非常简单的模型

class Lifestyle < ActiveRecord::Base
  attr_accessible :name
  has_and_belongs_to_many :profiles
end
Run Code Online (Sandbox Code Playgroud)

与...有has_and_belongs_to_many关系的Profile

class Profile < ActiveRecord::Base
  attr_accessible ...

  belongs_to :occupation

  has_and_belongs_to_many :lifestyles
  accepts_nested_attributes_for :lifestyles
end
Run Code Online (Sandbox Code Playgroud)

I want to use ActiveAdmin to edit the Profile object, but also assign Lifestyles to a profile. It should be similar to dealing with belongs_to :occupation, as this is sorted out automatically by ActiveAdmin to a dropbox with the options pre-filled with available occupations.

I've tried to use the has_many form builder method, but that only got me to show a form to type in the name of the Lifestyle and on submission, it returned an error.

    f.object.lifestyles.build
    f.has_many :lifestyles do |l|
      l.input :name
    end
Run Code Online (Sandbox Code Playgroud)

Error I get:

Can't mass-assign protected attributes: lifestyles_attributes
Run Code Online (Sandbox Code Playgroud)

The perfect way for me would be to build several checkboxes, one for each Lifestyle in the DB. Selected means that the lifestyle is connected to the profile, and unselected means to delete the relation.

我非常怀疑使用ActiveAdmin可以实现这一点,并且无需创建非常复杂的逻辑来处理这个问题.如果你提出自己的意见并告诉我是否应该采取这种方式或采取不同的方式,我将非常感激.

Cri*_*ian 18

经过一番研究,我准备回答我自己的问题.

首先,我要感谢@Lichtamberg建议修复.然而,这只会使事情变得复杂(关于安全性,但在这种情况下不是问题),并且无法帮助我达到理想的解决方案.

挖掘更多,我发现这是Rails中一个非常常见的场景,它实际上是在Ryan Bates的第17号电视剧中解释的.

因此,在Rails中,如果您有has_and_belongs_to_many(简短形式HABTM)关联,则可以通过此方法轻松设置其他关联对象的ID:

profile.lifestyle_ids = [1,2]
Run Code Online (Sandbox Code Playgroud)

如果你为lifestyle_ids设置attr_accessible,这显然适用于表单:

class Profile < ActiveRecord::Base
  attr_accessible :lifestyle_ids
end
Run Code Online (Sandbox Code Playgroud)

在ActiveAdmin中,因为它使用Formtastic,您可以使用此方法输出正确的字段(在本例中为复选框):

f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all
Run Code Online (Sandbox Code Playgroud)

此外,我已经简化了我的表单视图,所以它现在只是这样:

  form do |f|
    f.inputs # Include the default inputs
    f.inputs "Lifestlyes" do # Make a panel that holds inputs for lifestyles
      f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all # Use formtastic to output my collection of checkboxes
    end
    f.actions # Include the default actions
  end
Run Code Online (Sandbox Code Playgroud)

好的,现在这在视图中完美呈现,但如果我尝试提交我的更改,它会给我这个数据库错误:

PG::Error: ERROR:  null value in column "created_at" violates not-null constraint
: INSERT INTO "lifestyles_profiles" ("profile_id", "lifestyle_id") VALUES (2, 1) RETURNING "id"
Run Code Online (Sandbox Code Playgroud)

我发现这是因为Rails 3.2不会自动更新HABTM关联表的时间戳(因为它们是额外的属性,而Rails只处理_id属性).

有两种解决方案可以解决这个问题:

  1. 将关联转换为hm:t(has_many, :through =>)
  2. 或者从表中删除时间戳

我要去2)因为我永远不需要时间戳或任何额外的属性.

我希望这有助于其他人遇到同样的问题.

编辑: @cdesrosiers最接近解决方案但我在阅读之前已经写过这个答案.无论如何,这仍然很棒.我学到了很多东西.