рельсы проверяют вложенные атрибуты - PullRequest
3 голосов
/ 20 июня 2010

У художников много событий.События имеют много художников.Объединение между этими двумя моделями называется исполнениями.

В настоящее время форма события создает исполнение, но создает нового исполнителя для каждого исполнителя, добавляемого в форму события.

Я хотел бы, чтобы форма события:

  1. Проверка того, что Исполнитель может быть добавлен в Событие только один раз
  2. Если Исполнитель с таким именем уже существует в таблице Исполнителей, создайте ассоциацию в таблице объединения (Исполнения), но не создавать другого Исполнителя
  3. Если Исполнителя с таким именем еще не существует, создайте его и Исполнение

Я попытался добавить 'validates_uniqueness_of: name'to artist.rb, но это не позволяет сохранить событие.Объединение (исполнение) должно быть создано, если оно еще не существует, и исполнитель должен быть создан, если он еще не существует, но существование исполнителя не должно препятствовать созданию объединения / ассоциации.

event.rb

validates_presence_of :name, :location
has_many :performances, :dependent => :destroy
has_many :artists, :through => :performances
accepts_nested_attributes_for :artists, :reject_if => proc {|a| a['name'].blank?},     :allow_destroy => true

artist.rb

has_many :performances
has_many :events, :through => :performances

perfomance.rb

belongs_to :artist
belongs_to :event

events_controller.rb

def create
  @event = Event.new(params[:event])

  respond_to do |format|
    if @event.save
      flash[:notice] = 'Event was successfully created.'
      format.html { redirect_to(admin_events_url) }
      format.xml  { render :xml => @event, :status => :created, :location => @event }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @event.errors, :status => :unprocessable_entity }
    end
  end
end

_form.html.erb

<% form_for([:admin,@event]) do |f| %>
<p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
</p>
<p>
    <%= f.label :location %><br/>
    <%= f.text_field :location %>
</p>
<p>
    <%= f.label :date %><br />
    <%= f.date_select :date %>
</p>
<p>
    <%= f.label :description %><br />
    <%= f.text_area :description %>
</p>
<% f.fields_for :artists do |builder| %>
    <%= render 'artist_fields', :f => builder %>
<% end %>
<p><%= link_to_add_fields "Add Artist", f, :artists %></p>
<p>
    <%= f.submit 'Submit' %> <%= link_to 'Cancel', admin_events_path %>
</p>
<% end %>

artist_fields.html.erb

<p class="fields">   
<%= f.label :name, "Artist"%><br/>
<%= f.text_field :name %>
<%= link_to_remove_fields "remove", f %>
</p>

Ответы [ 2 ]

1 голос
/ 06 июля 2010

Вы действительно должны взглянуть на эти Railscasts:

  1. Не создавайте разных художников. Просто используйте существующий (или создайте при необходимости):

    http://railscasts.com/episodes/167-more-on-virtual-attributes

  2. Вы также можете проверить эти вложенные формы Railscasts (часть первая связана здесь):

    http://railscasts.com/episodes/196-nested-model-form-part-1

  3. Для проверки вы можете просто предложить метод, который будет выполнять проверку только один раз за событие. Что-то вроде (на Event.rb):

    validate :artists_appear_just_once
    
    private
    def artists_appear_just_once
      self.artists.size == self.artists.uniq.size
    end
    

В качестве альтернативы вы можете сделать так, чтобы artits появлялся только один раз по умолчанию, используя uniq! Метод перед сохранением. Просто вызовите ловушку before_save и обработайте массив художников ...

 before_save :make_artists_unique

 private
 def make_artists_unique
   artists.uniq!
 end

Надеюсь, я понял, что вам нужно: P

1 голос
/ 21 июня 2010

У вас есть процедура отклонения атрибутов художника, если имя пустое: вы также можете отклонить его, если художник уже существует в модели, но это не решает проблему дубликатов художников. По сути, вы хотите сделать find_or_create_by_name при добавлении их в ваше событие.

Я думаю, что в вашем случае было бы лучше определить собственный метод artist_attributes= вместо того, чтобы полагаться на accepts_nested. Таким образом, вы можете выполнить поиск для каждого имени исполнителя и добавлять его только по мере необходимости:

def artist_attributes=(params)
  if existing_artist = Artist.find_by_name(params[:name])
    self.artists << existing_artist unless self.artists.include? existing_artist
  else
    self.build_artist(params)
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...