Reputation: 3
I have an app including Dog, Events and Booking models. Each event has many bookings, each dog has many bookings but each booking belong to an event and has one dog.
On the view for Events it should list all bookings with that id and the details of the linked dog.
I was having a lot of trouble with "Rails Error: undefined method ` ' for nil:NilClass" but then realised the problem was that it is retrieving a list of dogs based on the booking id and not the dog_id field.
In the console I can see that this is the SQL query being generated and what I think is the source of the error but I can't figure out why:
SELECT "dogs".* FROM "dogs" INNER JOIN "bookings" ON "dogs"."id" = "bookings"."id" WHERE "bookings"."event_id" = ? [["event_id", 1]]
I've spent days combing Stack Overflow and Google to figure out where I've gone wrong but am a bit lost. Here are the models:
class Event < ActiveRecord::Base
has_many :bookings
has_many :dogs, :through => :bookings, :foreign_key => "dog_id"
end
class Dog < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 255 }
validates :breed, presence: true, length: { maximum: 255 }
belongs_to :owner
validates :owner_id, presence: true
has_many :bookings
has_many :events, through: :bookings, :foreign_key => "event_id"
end
class Booking < ActiveRecord::Base
belongs_to :event
has_one :dog, :foreign_key => "id"
validates :event_id, presence: true
validates :dog_id, presence: true
end
The controllers
class EventsController < ApplicationController
before_action :logged_in_user
def index
@events = Event.paginate(page: params[:page])
end
def show
@event = Event.find(params[:id])
@bookings = @event.bookings.paginate(page: params[:page])
...
class DogsController < ApplicationController
def show
@dog = Dog.find(params[:id])
end
def new
@dog = Dog.new
end
...
class BookingsController < ApplicationController
end
And finally the show view for Events and the _booking partial it renders:
<% provide(:title, @event.name) %>
<div class="row">
<aside class="col-md-4">
<section class="event_info">
<h1><%= @event.name %></h1> - <%= @event.category %>
<span><b><%= @event.date %></b> <%= @event.start %> - <%= @event.finish %> </span>
<span><b>Location:</b> <%= @event.location %></span>
<%-# Calculated occupany figure goes in line below. -%>
<span><b>Capacity: </b>X / <%[email protected] %></span>
</section>
</aside>
<div class="col-md-8">
<section class="event_notes_header">
Description
</section>
<section class="event_notes_body">
<%= @event.description %>
</section>
<section class="event_notes_header">
Notes
</section>
<section class="event_notes_body">
Event notes will be displayed here...
</section>
</div>
<div class="col-md-8">
<% if @event.bookings.any? %>
<h3>Bookings (<%= @event.bookings.count %>)</h3>
<ol class="bookings">
<%= render @bookings %>
</ol>
<%= will_paginate @bookings %>
<% end %>
</div>
</div>
_booking.html.erb
<li id="booking<%= booking.id %>">
<span class="event"><%= link_to booking.event.name, booking.event %></span>
<span class="dog"><%= link_to booking.dog.name, booking.dog %></span>
</li>
Apologies if I've missed anything or if the code has gotten a little convoluted - this is where I am after several days of plugging stuff in and out to try and fix this error.
Upvotes: 0
Views: 59
Reputation: 4147
The first problem I see here is in your associations (and I believe it's the main reason this crap is happening). Your Booking model is a typical joining model for a has_many :through
association. But the inner associations are not set properly. If you look at the association between Dog
and Booking
, you'll notice that there are two has_*
paths and no belongs_to
path (wich needs to be on the model, that has the dog_id
in it, in this case the Booking
model).
So your first step is to set the associations properly. You should change
has_one :dog, :foreign_key => "id"
to
belongs_to :dog
Second thing I recommend you do with those associations is getting rid of those foreign_key
calls, that might just confuse you and are not needed as long as you're folowing conventions.
Upvotes: 1