Reputation: 55
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
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
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.
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