Question

I'm working on an app to book different types of tours. I have 3 tables - 1) reservations 2) tours 3) join table (reservations_tours). I'm trying to pass the tour id to reservations, but keep getting this error:

Couldn't find Tour without an ID
app/controllers/reservations_controller.rb:12:in `create'

Reservations controller:

class ReservationsController < ApplicationController

  def index
  end

  def new
    @reservation = Reservation.new
    @tour = Tour.find(params[:tour_id])
  end

  def create
    @tour = Tour.find(params[:tour_id])
    @reservation = Reservation.new(reservation_params)
    #@reservation.tour = @tour # associate @reservation with video
    if @reservation.save
      Stripe.api_key = ENV["STRIPE_SECRET_KEY"]
      Stripe::Charge.create(
        :amount => 9000, # amount in cents, again
        :currency => "usd",
        :card => params[:stripeToken]    # Get the credit card details submitted by the form
      )
      flash[:success] = "Your reservation has been booked for #{@reservation.date} for #{@reservation.passengers} person(s). Please save this info."
      redirect_to new_tour_path
    else
      render 'new'
    end
  end

  private

  def reservation_params
    params.require(:reservation).permit(:date, :passengers)
  end
end

Tours controller:

class ToursController < ApplicationController
  def index
  end

  def new
    @tour = Tour.new
  end

  def create
    @tour = Tour.new(tours_params)
    if @tour.save
      flash[:success] = "Tour #{@tour.name} has been successfully added."
      redirect_to new_tour_path
    else
      flash[:error] = "The tour #{@tour.name} was not successfully saved. Please try again"
      render 'new'
    end
  end

  def show
    @tour = Tour.find_by(id: params[:id])
    @reservation = Reservation.new
  end

  def edit
  end

  def update
  end

  private

  def tours_params
    params.require(:tour).permit(:name, :amount)
  end
end

Reservations view (new)

= javascript_include_tag "https://js.stripe.com/v2/"

= javascript_include_tag 'payment'

:javascript
  Stripe.setPublishableKey("#{ENV['STRIPE_PUBLISHABLE_KEY']}");

.container
  .row
    .col-md-9
      %h2= @tour.name
    .col-md-3
      %p= @tour.amount

      = bootstrap_form_for(@reservation, html: { class: 'form-horizontal', id: 'payment-form'}) do |f|
        = f.alert_message 'Please fix the errors below:'
        = f.text_field :date
        = f.text_field :passengers
        %fieldset.credit_card
          %span.payment-errors
        .control-group
          = label_tag :card_number, 'Credit card number:', class: 'control-label'
          .controls
            = text_field_tag :card_number, nil, name: nil, class: 'span3', data: {stripe: 'number'}
        .control-group
          = label_tag :security_code, 'Security code:', class: 'control-label'
          .controls
            = text_field_tag :security_code, nil, name: nil, class: 'span3', data: {stripe: 'cvc'}
        .control-group
          = label_tag :exp_date, 'Expiration:', class: 'control-label'
          .controls
            = select_month(Date.today, {add_month_numbers: true},  class: 'span2', data: {stripe: 'exp-month'})
            = select_year(Date.today.year, {start_year: Date.today.year, end_year: Date.today.year + 4}, class: 'span1', data: {stripe: 'exp-year'})
        %fieldset.actions.control-group
          .controls
            = f.submit 'Sign up'

Reservation model:

class Reservation < ActiveRecord::Base
  has_many :reservations_tours, foreign_key: 'reservation_id'
  has_many :tours, through: :reservations_tours
end

Tour model

class Tour < ActiveRecord::Base
  has_many :reservations_tours, foreign_key: 'tour_id'
  has_many :reservations, through: :reservations_tours
end

Join table

class ReservationsTours < ActiveRecord::Base
  belongs_to :reservation, foreign_key: 'reservation_id'
  belongs_to :tour, foreign_key: 'tour_id'
end

Routes:

Rails.application.routes.draw do
  resources :reservations, only: [:new, :create]
  resources :tours
end
Was it helpful?

Solution

You are trying to create a non existing relation. You have two basic options to create a relation in rails:

  1. Provide a dropdown with existing tours in the reservations form

    f.collection_select(:tour_id, Tour.all, :id, :name)

    it will become available in the params[:reservation] array. You will have to permit the tour_id param in reservation_params

  2. make a nested resource for reservations in config/routes.rb.

    resources :tours do
      resources :reservations
    end
    

    which will give you a POST url like /tours/:tour_id/reservations and provide you with params[:tour_id]

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top