简体   繁体   English

rails验证嵌套属性

[英]rails validate nested attributes

Artists have many Events. 艺术家有很多活动。 Events have many Artists. 活动有很多艺术家。 The join between these two models is called Performances. 这两个模型之间的连接称为性能。

Currently the Event form creates the Performance but creates a new artist for each Artist added to the Event form. 目前,“事件”表单会创建“演奏组”,但会为添加到“事件”表单的每个艺术家创建新的艺

I would like the Event form to: 我希望活动表格能够:

  1. Validate that an Artist can only be added to an Event once 验证艺术家只能添加到事件一次
  2. If an Artist with the same name already exists in the Artists table, create the association in the join table (Performances), but do not create another Artist 如果Artists表中已存在具有相同名称的Artist,请在连接表(Performances)中创建关联,但不要创建另一个Artist
  3. If an Artist with the same name does not already exist, create it and the Performance 如果尚不存在具有相同名称的Artist,请创建它和Performance

I've tried adding 'validates_uniqueness_of :name' to artist.rb but this prevents the event from being saved. 我已经尝试将“validates_uniqueness_of:name”添加到artist.rb,但这可以防止事件被保存。 The join (performance) should be created if it does not already exist and the artist should be created if it does not already exist, but the existence of the artist should not prevent the join/association from being created. 如果连接(性能)尚不存在,则应创建连接(性能),如果尚未存在,则应创建艺术家,但艺术家的存在不应阻止创建连接/关联。

event.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 artist.rb

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

perfomance.rb perfomance.rb

belongs_to :artist
belongs_to :event

events_controller.rb 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.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 artist_fields.html.erb

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

You have the proc to reject artist attributes if the name is blank: you could also reject it if the artist already exists on the model, but that doesn't solve the problem of duplicate Artists. 如果名称为空白,您可以使用proc来拒绝艺术家属性:如果艺术家已经存在于模型中,您也可以拒绝它,但这并不能解决重复艺术家的问题。 Essentially you want to do a find_or_create_by_name when adding them to your event. 基本上,您希望在将事件添加到事件时执行find_or_create_by_name。

I think in your case it would be better to define your own artist_attributes= method instead of relying on accepts_nested. 我认为在你的情况下,最好定义自己的artist_attributes=方法而不是依赖accepts_nested。 This way you can do your lookup for each artist name and add it only as needed: 这样,您可以查找每个艺术家名称并仅根据需要添加它:

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

You should really take a look at these Railscasts: 你应该看看这些Railscasts:

  1. Don't create various artists. 不要创造各种艺术家。 Just use the existing one (or create if needed): 只需使用现有的(或创建,如果需要):

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

  2. You can also check these nested form railscasts (part one linked here): 您还可以检查这些嵌套表单railscast(这里链接的第一部分):

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

  3. For the validation, you can just have a method do the just-once-in-the-event validation for you. 对于验证,您可以让方法为您进行一次性事件验证。 Something like (at Event.rb): 像(在Event.rb)的东西:

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

Alternatively you can make the artsits appear just once by default using the uniq! 或者,您可以使用uniq默认显示artsits一次! method before saving. 保存前的方法。 Just call a before_save hook and process the artists array... 只需调用before_save钩子并处理艺术家数组......

 before_save :make_artists_unique

 private
 def make_artists_unique
   artists.uniq!
 end

Hope I got what you need right :P 希望我得到你需要的东西:P

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM