【问题标题】:accpets_nested_attributes_for, HABTM, and find_or_createaccpets_nested_attributes_for、HABTM 和 find_or_create
【发布时间】:2011-06-23 02:48:06
【问题描述】:

我正在使用 Rails 的 accept_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

【问题讨论】:

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


    【解决方案1】:

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

    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
    

    【讨论】:

    • 在我的情况下 def get_people self.people.map! do |person| Person.find_or_create_by_name(person.name) end end 不工作,但 def get_people self.people = self.people.collect do |person| self.people = self.people.collect do |person| Person.find_or_create_by_name(person.name) @987653330@ @span76 工作! >
    【解决方案2】:

    如果 'name' 确实是 License 的唯一属性,如果它尚不存在,则可用于创建它,那么您可以通过 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
    

    然后您可以将licence_name 作为新Package 对象的属性传入,例如使用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 %>
    

    您必须在控制器的创建操作中做更多的工作才能使其工作,但这应该不会太难。

    【讨论】:

    • 我正在使用 describe "licenses" do it "should only create a single instance of a license" do @package.licenses.create!(:name => "TEST") count = License.all.count @package.licenses.create!(:name => "TEST") License.all.count.should == count end end即使在执行了上述之后,脚本似乎仍然失败。
    • Roberto 对此有什么帮助吗?它似乎仍然不起作用。有没有办法在许可证级别而不是包级别实现相同的逻辑?
    猜你喜欢
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多