rails验证嵌套属性

时间:2010-06-19 21:24:56

标签: ruby-on-rails validation

艺术家有很多活动。活动有很多艺术家。这两个模型之间的连接称为Performances。

目前,“事件”表单会创建“演奏组”,但会为添加到“事件”表单的每个艺术家创建一个新的艺术家。

我希望活动表格能够:

  1. 验证艺术家只能添加到活动一次
  2. 如果Artists表中已存在具有相同名称的艺术家,请在联接表(演出)中创建关联,但不要创建其他艺术家
  3. 如果尚不存在具有相同名称的艺术家,请创建它和演奏
  4. 我尝试将'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>
    

2 个答案:

答案 0 :(得分:1)

如果名称为空白,您可以使用proc来拒绝艺术家属性:如果艺术家已经存在于模型中,您也可以拒绝它,但这并不能解决重复艺术家的问题。基本上,您希望在将事件添加到事件时执行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

答案 1 :(得分:1)

你应该看看这些Railscasts:

  1. 不要创作各种艺术家。只需使用现有的(或创建,如果需要):

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

  2. 您还可以检查这些嵌套表单railscast(此处链接的第一部分):

    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
    
  4. 或者,您可以使用uniq默认显示artsits一次!保存前的方法。只需调用before_save挂钩并处理艺术家数组......

     before_save :make_artists_unique
    
     private
     def make_artists_unique
       artists.uniq!
     end
    

    希望我得到你需要的东西:P