Mam dwa modele:
class Booking < ActiveRecord::Base
belongs_to :guest
end
class Guest < ActiveRecord::Base
has_many :bookings
end
Kontroler do booking wygląda tak:
class BookingsController < ApplicationController
def index
@bookings = Booking.includes(:guest).all
end
end
Zawartość dwóch tabeli tj. bookings i guests chcę wyświetlić w jednym widokiem:
/index.html.erb
lista
<% @bookings.each do |booking| %>
<%= booking.id %>
<%= booking.date_in %>
<%= booking.date_out %>
<%= booking.guest.email %>
<%= booking.guest.name %>
<% end %>
jednak cały czas dostaję informację o błędzie:
undefined method `email’ for nil:NilClass
Jak widzisz booking jest nilem, masz jakieś dane zseedowane do bazy? Wrzuć binding.pry do controllera, zobacz do czego masz dostęp, sprawdź asociację w terminalu (np. User.last.guest.email)
W obydwóch tablicach znajdują się dane. Błąd undefined method `email’ for nil:NilClass pojawia się tylko dla:
<%= booking.guest.email %>
<%= booking.guest.name %>
booking.guest jest nil-em
Pokaż migracje i seeds.rb bo nil jednak występuje w booking.guest_id
Użyj <%= booking.guest.try(:email) %>
Jeżeli quest będzie nil’em , to funkcja try()
zwróci pusty ciąg zamiast błędu w przypadku konstrukcji:
booking.guest.email
<%= booking.guest.try(:email) %> nie zwraca danych z tablicy guests (tablica zawiera wpisane dane), zwraca jedynie jeden rekord z tablicy bookings:
lista
1
10-10-2017
12-10-2017
moje migracje:
class CreateGuests < ActiveRecord::Migration
def change
create_table :guests do |t|
t.string :name
t.string :email
t.timestamps null: false
end
end
end
class CreateBookings < ActiveRecord::Migration
def change
create_table :bookings do |t|
t.string :date_in
t.string :date_out
t.belongs_to :guest, index: true, foreign_key: true
t.timestamps null: false
end
end
end
Istnieją odwołania w tabeli bookings do usuniętych wpisów tabeli guests … brak obsługi dependent: … w Guest AR
chociaż nie wiem czy są możliwe takie odwołania patrząc na:
jest możliwe FK sprawdzany tylko przy create/update w bookings
Musisz w końcu zdać sobie sprawę, że “includes” nie tworzy połączenia inner join
między tabelami, a jedynie outer join
. Poszukaj na czym polega różnica, a zrozumiesz, dlaczego masz nil po jednej ze stron złączenia.
Czyli idąc w ślad za dokumentacją[1] zamiast konstrukcji:
@bookings = Booking.includes(:guest).all
powinno być:
@bookings = Booking.joins(:guest)
Zgadza się?
[1] http://guides.rubyonrails.org/active_record_querying.html
Działą z następującym zapisem:
@bookings = Booking.joins(:guest)
ale dodatkowo okazało się że w tabeli Bookings kolumna guest_id była nil’em . Czyli brakowało “połączenia” pomiędzy tabelami Bookings-Guests.
Bardzo wszystkim dziękuję za poświęcony czas i podpowiedzi.