accpets_nested_attributes_for,HABTM和find_or_create

时间:2011-06-23 02:48:07

标签: ruby-on-rails-3 validation activerecord has-and-belongs-to-many

我正在使用Rails的accepts_nested_attributes_for方法取得了巨大的成功,但是如果记录已经存在,我如何才能创建新记录?

我找到了这篇文章accepts_nested_attributes_for with find_or_create?,但在我的案例中似乎没有用。

我在软件包和许可证之间存在多对多的关系。表中应该只有一个许可证实例,例如:我不应该有两个名为“Awesome”的许可证

所需的功能是,当我创建包时,将许可证名称作为属性传递,如果名称尚不存在,则创建新许可证或在现有许可证(使用提供的名称)之间创建关联包裹。

以下是我的模型的样子:

class Package < ActiveRecord::Base
  has_and_belongs_to_many :licenses, :autosave => true
  accepts_nested_attributes_for :licenses
end

class License < ActiveRecord::Base
  attr_accessible :name
  has_and_belongs_to_many :packages
  validates :name, :presence => true
end

2 个答案:

答案 0 :(得分:0)

如果'name'确实是用于创建它的唯一属性,如果它尚不存在,那么你可以通过Package类和虚拟属性的回调来实现:

class Package < ActiveRecord::Base
   has_and_belongs_to_many :licenses, :autosave => true
   before_create :set_license_from_name
   attr_accessor :license_name       

   protected

      def set_license_from_name
         self.licenses << Licence.find_or_create_by_name(self.license_name)
      end

end

然后你可以在新的Package对象中传递licence_name作为属性,例如使用text_box:

<%= form_for @package do |f| %>
  <%= f.text_field :license_name %>
<% end %>

可能不是世界上最好的用户界面。相反,您可能希望人们能够从下拉框中选择现有许可,或者键入新名称。有accepts_nested_attributes_for有用的地方:

class Package < ActiveRecord:Base
   has_and_many :licenses
   accepts_nested_attributes_for :licenses, :reject_if => :all_blank
end

class PackagesController < ApplicationController

   def new
      @package = Package.new
      @package.licenses.build
      @licenses = License.all
   end
end

在视图中:

<%= form_for @package do |f| %>

  License:
  <%= f.collection_select :license_id, @licenses, :id, :name, :include_blank => true %>

  or create a new one:
  <%= f.fields_for :licenses do |l| %>
     <%= l.text_field :name %>
  <% end %>
<% end %>

你必须在控制器的创建动作中做更多的工作才能使它工作,但它不应该太难。

答案 1 :(得分:0)

我遇到了类似的问题,并使用回调挂钩解决了这个问题。在我的情况下,我有一个状态模型和一个人模型。我想让用户在状态更新中标记多个人(人物),只需键入一个名称并创建一个新人(如果匹配的人不存在,这些人不会映射到用户,顺便说一下,它只是供私人使用)

class Person < ActiveRecord::Base
  has_and_belongs_to_many :statuses
end

Class Status < ActiveRecord::Base
  has_and_belongs_to_many :people
  accepts_nested_attributes_for :people
  before_save :get_people

  def get_people
    self.people.map! do |person|
      Person.find_or_create_by_name(person.name)
    end
  end
end