accpets_nested_attributes_for、HABTM 和 find_or_create

发布于 2024-11-16 18:31:48 字数 752 浏览 5 评论 0原文

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

I'm using Rails' accepts_nested_attributes_for method with great success, but how can I have it not create new records if a record already exists?

I have found this article, accepts_nested_attributes_for with find_or_create? but it doesn't seem to work in my case.

I have a many-to-many relationship between packages and licenses. There should only be one instance of a license in the table, for instance: I shouldn't have two licenses named, "Awesome"

The desired functionality is that when I create a package, passing in a license name as an attribute, create a new license if the name doesn't already exist or create an association between the existing license (with the provided name) and the package.

Here are what my models look like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

镜花水月 2024-11-23 18:31:48

我遇到了类似的问题并通过回调挂钩解决了它。在我的情况下,我有一个 Status 模型和一个 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

I had a similar issue and solved it with callback hooks. In my situation, I have a Status model and a Person model. I wanted to let users tag multiple people (Person) in their Status update by just typing in a names and it creating a new Person (these do no map to Users, btw, it's just for private use) if a matching one does not exist.

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
豆芽 2024-11-23 18:31:48

如果“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

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

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

但这可能不是世界上最好的 UI。相反,您可能希望人们能够从下拉框中选择现有许可证,或输入新许可证的名称。这是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 %>

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

If 'name' really is the only attribute of License that would be used to create it if it didn't already exist, then you could do this via a callback on the Package class and a virtual attribute:

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

Then you could could just pass in licence_name as an attribute on the new Package object, eg using a text_box:

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

Probably not the best UI in the world though. Instead, you may want people to be able to select an existing licence from a dropdown box, or type in the name of a new one. There is where accepts_nested_attributes_for is useful:

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

And in the view:

<%= 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 %>

You'll have to do a little bit more work in the create action of the controller to get this to work, but it shouldn't be too hard.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文