accpets_nested_attributes_for, HABTM и find_or_create - PullRequest
3 голосов
/ 23 июня 2011

Я использую Rails метод acceptpts_nested_attributes_for с большим успехом, но как мне сделать так, чтобы он не создавал новые записи, если запись уже существует?

Я нашел эту статью, acceptpts_nested_attributes_for с 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 голосов
/ 27 августа 2011

У меня была похожая проблема, и я решил ее с помощью обратных вызовов.В моей ситуации у меня есть модель Status и модель Person.Я хотел позволить пользователям отмечать нескольких людей (Person) в их обновлении Status, просто вводя имена и создавая нового Person (они не отображаются на Users, кстати, это только для личного использования), если соответствующего не существует.

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
0 голосов
/ 23 июня 2011

Если «имя» действительно является единственным атрибутом лицензии, который будет использоваться для его создания, если он еще не существует, то вы можете сделать это с помощью обратного вызова класса 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 %>

Вам нужно будет проделать немного больше работы в действии create контроллера, чтобы заставить его работать, но это не должно быть слишком сложно.

...