Rails 3, nested multi-level forms and has_many through

10,452

To be honest, i have never tried to edit or create a has_many :through in that way.

It took a little while, and had to fix the js inside formtastic_cocoon to get it working, so here is a working solution.

You need to specift the EventUser model, and then fill the User model (the other way round will never work).

So inside the models you write:

class Event < ActiveRecord::Base
  has_many :event_users
  has_many :users, :through => :event_users
  accepts_nested_attributes_for :users, :reject_if => proc {|attributes| attributes[:name].blank? }, :allow_destroy => true
  accepts_nested_attributes_for :event_users, :reject_if => proc {|attributes| attributes[:user_attributes][:name].blank? }, :allow_destroy => true
end

class EventUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :event
  accepts_nested_attributes_for :user
end

class User < ActiveRecord::Base
  has_many :events, :through => :event_users
  has_many :event_users
end

Then the views. Start with the events/_form.html.haml

= semantic_form_for @event do |f|
  - f.inputs do
    = f.input :name

    %h3 Users (with user-type)
    #users_with_usertype
      = f.semantic_fields_for :event_users do |event_user|
        = render 'event_user_fields', :f => event_user
      .links
        = link_to_add_association 'add user with usertype', f, :event_users

    .actions
      = f.submit 'Save'

(i ignore errors for now) Then, you will need to specify the partial _event_user_fields.html.haml partial (here comes a little bit of magic) :

.nested-fields
  = f.inputs do
    = f.input :user_type, :as => :hidden, :value => 'participating'
    - if f.object.new_record?
      - f.object.build_user
    = f.fields_for(:user, f.object.user, :child_index => "new_user") do |builder|
      = render("user_fields", :f => builder, :dynamic => true)

and to end the _user_fields partial (which does not really have to be a partial)

.nested-fields
  = f.inputs do
    = f.input :name

This should work. Do note that i had to update the formtastic_cocoon gem, so you will need to update to version 0.0.2.

Now it would be easily possible to select the user_type from a simple dropdown, instead of a hidden field, e.g. use

= f.input :user_type, :as => :select, :collection => ["Participator", "Organizer", "Sponsor"]

Some thoughts (now i proved it works):

  • this will always create new users on the fly, actually eliminating the need for the EventUser. Will you allow selecting existing users from a dropdown too?
  • personally i would turn it around: let users assign themselves to an event!
Share:
10,452
jonepatr
Author by

jonepatr

Hello

Updated on June 04, 2022

Comments

  • jonepatr
    jonepatr almost 2 years

    I'm trying to get it to work but it dosen't!

    I have

    class User < ActiveRecord::Base
      has_many :events, :through => :event_users
      has_many :event_users
      accepts_nested_attributes_for :event_users
    end
    
    class Event < ActiveRecord::Base
      has_many :event_users
      has_many :users, :through => :event_users
      accepts_nested_attributes_for :users
    end
    
    class EventUser < ActiveRecord::Base
      set_table_name :events_users
      belongs_to :event
      belongs_to :user
      accepts_nested_attributes_for :events
      accepts_nested_attributes_for :users
    end
    

    And also the table-layout

    event_users
      user_id
      event_id
      user_type
    events
      id
      name
    users
      id
      name
    

    And this is my form

    <%= semantic_form_for @event do |f| %>
      <%= f.semantic_fields_for :users, f.object.users do |f1| %>
        <%= f1.text_field :name, "Name" %>
        <%= f1.semantic_fields_for :event_users do |f2| %>
          <%= f2.hidden_field :user_type, :value => 'participating' %>
        <% end %>
      <% end %>
    <%= link_to_add_association 'add task', f, :users %>
    <% end %>
    

    The problem is that if I create a new user this way, it doesn't set the value of user_type (but it creates a user and a event_users with user_id and event_id). If I go back to the edit-form after the creation of a user and submit, then the value of user_type is set in events_users. (I have also tried without formtastic) Any suggestions? Thanks!

    ----edit----

    I have also tried to have the event_users before users

    <%= semantic_form_for @event do |f| %>
      <%= f.semantic_fields_for :event_users do |f1| %>
        <%= f1.hidden_field :user_type, :value => 'participating' %>
        <%= f1.semantic_fields_for :users do |f2| %>
          <%= f2.text_field :name, "Name" %>
        <% end %>
      <% end %>
    <%= link_to_add_association 'add task', f, :event_users %>
    <% end %>
    

    but then it only throws me an error:

    User(#2366531740) expected, got ActiveSupport::HashWithIndifferentAccess(#2164210940)

    --edit--

    the link_to_association is a formtastic-cocoon method (https://github.com/nathanvda/formtastic-cocoon) but I have tried to do other approaches but with the same result

    ---edit----

    def create
      @event = Event.new(params[:event])
      respond_to do |format|
        if @event.save
          format.html { redirect_to(@event, :notice => 'Event was successfully created.') }
          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