views:

38

answers:

1

Artists have many Events. Events have many Artists. The join between these two models is called Performances.

I'm trying to associate Artists with Events on the Event add/edit page. I would like to be able to add an Artist only if it doesn't exist, and create the join (performance) regardless. An Artist should be associated with an Event only once.

It was suggested that I use find_or_create_by_name instead of accepts_nested_attributes_for.

I'm following the Railscasts #102 instructions for Auto-Complete which say to use virtual attributes. I haven't even gotten to the auto-complete part, just trying to get find_or_create_by_name working.

I'm getting "undefined method `artist_name' for #" on the Event edit and new pages. In the Railscast, Ryan gets an undefined method before he adds the methods to the model. But I have the method in the Model.

No idea what to do.

event.rb

validates_presence_of :name, :location
validates_uniqueness_of :name

validates_associated :performances

has_many :performances, :dependent => :delete_all
has_many :artists, :through => :performances

#accepts_nested_attributes_for :artists, :reject_if => proc {|a| a['name'].blank?}, :allow_destroy => true

def artist_name
  artist.name if artist
end

def artist_name=(name)
  self.artist = Artist.find_by_name(name) unless name.blank?
end

artist.rb

validates_presence_of :name

has_many :mixes  

has_many :performances, :dependent => :delete_all
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 :artist_name, "Artist"%><br/>
<%= f.text_field :artist_name %>
<%= link_to_remove_fields "remove", f %>
</p>
A: 

Personally I would go back to accepts_nested_attributes_for, ryan bates method there was in the days before nested attributes.

In your controller do something like:

def new
  @event = Event.find params[:id]
  @artist = @event.artists.build


def edit
  @event = Event.find params[:event_id]
  @artist = @event.artists.find params[:user_id]

While in the view

...
<% f.fields_for :artists, @artist do |builder| %>    
...
mark