Rails check nested attributes

Artists have many events. Events have many artists. The connection between the two models is called Performances.

Currently, the Event form creates Performance, but creates a new artist for each artist added to the Event form.

I would like the Event form:

  • Confirm that the Contractor can only be added to the event once
  • If the same name already exists in the Artists table, create an association in the Performances table, but do not create another Artist
  • If the same name does not exist, create it and Performance

I tried adding 'validates_uniqueness_of: name' to artist.rb, but this prevents the event from being saved. A connection (performance) should be created if it does not exist yet, and an artist should be created if it does not exist yet, but the existence of an artist should not impede the creation of a connection / association.

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>
+3
source share
2 answers

You have proc to reject artist attributes if the name is empty: you can also reject it if the artist already exists in the model, but this does not solve the problem of duplicate artists. Essentially, you want to make find_or_create_by_name when you add them to your event.

, 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

Railscasts:

, uniq! . hook before_save ...

 before_save :make_artists_unique

 private
 def make_artists_unique
   artists.uniq!
 end

, , : P

+1

Source: https://habr.com/ru/post/1750845/


All Articles