joeyc
joeyc

Reputation: 55

How to create an event registration rails structure

I have what I thought is a simple event signup application. A user registers for the site and then can select an event, apply to participate in that event by updating some fields that are on the user model (At this point, just a first_name). A User can attend many Events, but must register (Participation) for each one. An Event can have many Users through Participations. Any help is greatly appreciated!

There are currently three models:

# user.rb
class User < ActiveRecord::Base
  has_many :participations
  has_many :events, through: :participations

  devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :trackable, :validatable
end

# event.rb 
class Event < ActiveRecord::Base
  has_many :participations
  has_many :users, through: :participations

end

# And a join table: participation.rb
class Participation < ActiveRecord::Base
  belongs_to :user
  belongs_to :event

  accepts_nested_attributes_for :user
end

Here's my routes file: routes.rb

Rails.application.routes.draw do
  mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
  devise_for :users

  root 'events#index'

  resources :events do
    resources :participations
  end

  resources :users
end

And I think the only applicable controller: participations_controller.rb

class ParticipationsController < ApplicationController
  def index
  end

  def new
    @participation = Participation.new
    @user = current_user
    @event = Event.find(params[:event_id])
  end

  def create
    @participation = Participation.new(participation_params)
    if @participation.save
      redirect_to events_path, notice: "You are registered!"
    else
      render action: 'new'
    end
  end

private

  def participation_params
    params.require(:participation).permit(:status, :user_attributes => [:id, :first_name])
  end
end

The form should simply create a new participation based on the event_id, set its status, and update the user_attributes. views/participations/new.html.erb

<%= form_for @participation, url: {action: "create"} do |f| %>
  <%= f.label :status %><br />
  <%= f.text_field :status %>

  <%= f.fields_for :user, current_user do |builder| %>
    <fieldset>
      <%= builder.label :first_name, "First Name" %><br />
      <%= builder.text_field :first_name %><br />
    </fieldset>
  <% end %>

  <%= f.submit "Register" %>
<% end %>

Unfortunately, completing the form returns a 404 error with a missing participation_id.

Started POST "/events/1/participations" for ::1 at 2015-11-24 21:26:35 -0600
Processing by ParticipationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0vVTyeGwGUheZPbOoMeyUvr2ciJG2OpXwqToc2pYLr2HXDMhogX8llESiG8Z4Cc5Pq5sBmiHi43rvjHka7K3yA==", "participation"=>{"status"=>"done", "user_attributes"=>{"first_name"=>"sone", "id"=>"1"}}, "commit"=>"Register", "event_id"=>"1"}
Completed 404 Not Found in 3ms (ActiveRecord: 0.0ms)

ActiveRecord::RecordNotFound - Couldn't find User with ID=1 for Participation with ID=:

Upvotes: 0

Views: 1553

Answers (2)

joeyc
joeyc

Reputation: 55

Thanks to @ssoulless for pointing me in to the final solution.

Ultimately I was able to update the controller action to associate the user and then use the participation_params to update the participation[:user].

# participations_controller.rb

...
  def create
    @participation = Participation.new(status: participation_params[:status])
    @participation.mission_id = params[:mission_id]
    current_user.update_attributes(participation_params[:user_attributes])
    @participation.user = current_user

    if @participation.save
      redirect_to missions_path, notice: "You are registered!"
    else
      render action: 'new'
    end
  end

private

  def participation_params
    params.require(:participation)
          .permit(:id, :status, user_attributes: [:id, :first_name])
  end

I like this approach a bit more since it hides the user attributes in a private method. Also for anyone that this might help, when using accepts_nested_attributes_for in your model, you need to add it to the strong_params #permit parameters (Don't forget the ID!).

Upvotes: 0

svelandiag
svelandiag

Reputation: 4320

Well as described here, what is happening is that when you pass an id to the nested model, accepts_nested_attributes will look and try to update the model you are looking for.

So at that moment there is not such association between current user and the Participation you want to create, that's why you get the error:

Couldn't find User with ID=1 for Participation with ID=:

That means there is not such user with ID=1 associated with your participation

My suggestion:

Instead of add nested attributes for user, why not just add the fields you need to the Participation model?

Add the first_name attribute to your participation model and in your controller do the following:

class ParticipationsController < ApplicationController
  def index
  end

  def new
    @participation = Participation.new
    @participation.user = current_user
    @participation.event = Event.find(params[:event_id])
  end

  def create
    @participation = Participation.new(participation_params)
    @participation.user = current_user
    @participation.event = Event.find(params[:event_id])
    if @participation.save
      redirect_to events_path, notice: "You are registered!"
    else
      render action: 'new'
    end
  end

private

  def participation_params
    params.require(:participation).permit(:status, :first_name)
  end
end

Then in your form you can just make a normal first_name input:


<fieldset>
  <%= f.label :first_name, "First Name" %><br />
  <%= f.text_field :first_name %><br />
</fieldset>

Try that and let me know, by the way do not forget to remove the accepts_nested_attributes from Participation model, and make sure your migrations are correctly set up for match the associations you have. I hope have helped you.

Update

If you do not want to persist user information in the Participation then you can just add attribute accessors to your Participation model, and store information in your current_user in your create action:

#app/models/participation.rb
class Participation < ActiveRecord::Base
  belongs_to :user
  belongs_to :event

  attr_accessor :first_name, :whatever_other_attribute # You can add as many attributes you need.
end

Then just update information of your current_user in your create action:

#app/controllers/participations_controller
def create
    @participation = Participation.new(participation_params)
    @participation.user = current_user
    @participation.event = Event.find(params[:event_id])
    current_user.update_attributes first_name: @participation.first_name
    if @participation.save
      redirect_to events_path, notice: "You are registered!"
    else
      render action: 'new'
    end
  end

This way you store the information in the current_user instead of Participation, also this way you can easily customize the different information you will ask in the participation form.

Upvotes: 1

Related Questions