2010-06-19 179 views
3

藝術家有許多事件。活動有許多藝術家。這兩個模型之間的連接稱爲表演。rails驗證嵌套屬性

當前「活動」表單創建「績效」,但爲添加到「活動」表單的每位藝術家創建一位新藝術家。

我想事件的形式:

  1. 驗證一個藝術家只能被添加到一個事件一旦
  2. 如果具有相同名稱的藝術家的藝術家表已經存在,創建協會在連接表(表演),但不創造另一個藝術家
  3. 如果具有相同名稱的藝術家已經不存在,創建和性能

我試過加將'validates_uniqueness_of:name'賦給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> 

回答

1

你有PROC拒絕藝術家的屬性,如果名稱爲空:你也可以拒絕它,如果藝術家已經存在在模型上,但這並不能解決複製藝術家的問題。基本上你想在將它們添加到事件時執行find_or_create_by_name。

我認爲在你的情況下,最好定義你自己的artist_attributes=方法,而不是依賴於accepted_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 
+0

我一直在研究你提出什麼,但無法弄清楚如何開始。 – shalako 2010-07-05 17:48:55

+0

所以我需要在事件模型中添加一個自定義方法,並以某種方式合併find_or_create_by_name。我需要移除accept_nested_attributes_for嗎?我需要使用虛擬屬性,就像Ryan Bates在Railscasts#102中所建議的那樣? Before_save,after_save?我還沒有找到一個很好的例子,說明這些東西如何融合在一起。 – shalako 2010-07-05 17:58:24

+0

我添加了上面的artist_attributes =方法的示例。如果你想利用'f.fields_for:artist'的優勢,我認爲你仍然需要accepted_nested – 2010-07-13 23:41:03

1

你真的應該看看這些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 
    

或者,也可以使artsits使用uniq的默認顯示只有一次!方法保存之前。只需撥打一個before_save鉤和處理藝人陣...

before_save :make_artists_unique 

private 
def make_artists_unique 
    artists.uniq! 
end 

希望我得到了你所需要的正確的:P