Reputation: 703
The functionality I'm trying to build allows Users to Visit a Restaurant.
I have Users, Locations, and Restaurants models. Locations have many Restaurants.
I've created a Visits model with user_id and restaurant_id attributes, and a visits_controller with create and destroy methods.
Thing is, I can't create an actual Visit record. Any thoughts on how I can accomplish this? Or am I going about it the wrong way.
No route matches {:controller=>"restaurants", :location_id=>nil}
Routes:
location_restaurant_visits POST /locations/:location_id/restaurants/:restaurant_id/visits(.:format) visits#create
location_restaurant_visit DELETE /locations/:location_id/restaurants/:restaurant_id/visits/:id(.:format) visits#destroy
Model:
class Visit < ActiveRecord::Base
attr_accessible :restaurant_id, :user_id
belongs_to :user
belongs_to :restaurant
end
View:
<% @restaurants.each do |restaurant| %>
<%= link_to 'Visit', location_restaurant_visits_path(current_user.id, restaurant.id), method: :create %>
<% @visit = Visit.find_by_user_id_and_restaurant_id(current_user.id, restaurant.id) %>
<%= @visit != nil ? "true" : "false" %>
<% end %>
Controller:
class VisitsController < ApplicationController
before_filter :find_restaurant
before_filter :find_user
def create
@visit = Visit.create(params[:user_id => @user.id, :restaurant_id => @restaurant.id])
respond_to do |format|
if @visit.save
format.html { redirect_to location_restaurants_path(@location), notice: 'Visit created.' }
format.json { render json: @visit, status: :created, location: @visit }
else
format.html { render action: "new" }
format.json { render json: @visit.errors, status: :unprocessable_entity }
end
end
end
def destroy
@visit = Visit.find(params[:user_id => @user.id, :restaurant_id => @restaurant.id])
@restaurant.destroy
respond_to do |format|
format.html { redirect_to location_restaurants_path(@restaurant.location_id), notice: 'Unvisited.' }
format.json { head :no_content }
end
end
private
def find_restaurant
@restaurant = Restaurant.find(params[:restaurant_id])
end
def find_user
@user = current_user
end
end
Upvotes: 3
Views: 195
Reputation: 27374
I see a lot of problems here. The first is this line of code in your VisitController
's create
action (and identical line in your destroy
action):
@visit = Visit.create(params[:user_id => @user.id, :restaurant_id => @restaurant.id])
params
is a hash, so you should be passing it a key (if anything), not a bunch of key => value
bindings. What you probably meant was:
@visit = Visit.create(:user_id => @user.id, :restaurant_id => @restaurant.id)
Note that you initialize @user
and @restaurant
in before filter methods, so you don't need to access params
here.
This line of code is still a bit strange, though, because you are creating a record and then a few lines later you are saving it (if @visit.save
). This is redundant: Visit.create
initiates and saves the record, so saving it afterwards is pretty much meaningless. What you probably want to do is first initiate a new Visit
with Visit.new
, then save that:
def create
@visit = Visit.new(:user_id => @user.id, :restaurant_id => @restaurant.id)
respond_to do |format|
if @visit.save
...
The next thing I notice is that you have not initiated a @location
in your create
action, but you then reference it here:
format.html { redirect_to location_restaurants_path(@location), notice: 'Visit created.' }
Since you will need the location for every restaurant route (since restaurant
is a nested resource), you might as well create a method and before_filter
for it, like you have with find_restaurant
:
before_filter :find_location
...
def find_location
@location = Location.find(params[:location_id])
end
The next problem is that in your view your location_restaurant_path
is passed the id
of current_user
and of restaurant
. There are two problems here. First of all the first argument should be a location, not a user (matching the order in location_restaurant_path
). The next problem is that for the _path
methods, you have to pass the actual object, not the object's id. Finally, you have method: :create
, but the method
here is referring to the HTTP method, so what you want is method: :post
:
link_to 'Visit', location_restaurant_visits_path(@location, restaurant.id), method: :post
You'll have to add a find_location
before filter to your RestaurantController
to make @location
available in the view here.
There may be other problems, but these are some things to start with.
Upvotes: 1
Reputation: 23236
location_id
is nil
and the path definition doesn't say (/:location_id)
forcing a non-nil value there in order to route to that path; create a new route without location_id
if you can derive it from a child's attribute (i.e. a restaurant_id
refers to a Restaurant
which already knows its own location_id
).
Upvotes: 0