mLuby
mLuby

Reputation: 703

Create rails record from two ids

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.

Routing Error

No route matches {:controller=>"restaurants", :location_id=>nil}

Code:

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

Answers (2)

Chris Salzberg
Chris Salzberg

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

cfeduke
cfeduke

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

Related Questions