2012-11-05 17 views
5

Wciąż jestem nowy dla Rails, więc mam nadzieję, że to nie jest głupie pytanie.Uzyskiwanie dostępu do atrybów asocjacji has_one

Mam dwa modele: Użytkownik i Chore. Użytkownik has_one chore i Chore należy do Użytkownika

class User < ActiveRecord::Base 
    attr_accessible :chore_done, :email, :name, :phone 
    has_one :chore 

class Chore < ActiveRecord::Base 
    attr_accessible :name, :user_id 
    belongs_to :user 

W moim indeksu użytkownika, staram się pokazać wszystkich użytkowników i wyświetlać robota, który jest skojarzony z nim. Robię to poprzez przepuszczenie User.all do widzenia i przy użyciu .each iterację każdego użytkownika:

<% @users.each do |user| %> 
    <tr> 
    <td><%= user.name %></td> 
    <td><%= user.chore.name %></td> 
    <td><%= user.chore_done? %></td> 
    <td><%= link_to 'Edit', edit_user_path(user) %></td> 
    <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> 
    </tr> 
<% end %> 

Niestety, nie mogę uzyskać dostępu do atrybut name chore. Otrzymuję ten błąd:

undefined method `name' for nil:NilClass

Jeśli usunąć atrybut .Name, po prostu zwraca wskaźnik do obiektu chore.

Mam przeczucie, że ma to coś wspólnego z przekazaniem obiektu User.all do widoku, a następnie powtórzeniem tego. Po prostu dostęp do określonego obiektu użytkownika (na przykład User.find (1)) w konsoli, a następnie dostęp do user.chore.name działa poprawnie.

1.9.3-p194 :045 > user = User.find(4) 
    User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 4]] 
=> #<User id: 4, name: "Example User", email: "[email protected]", phone: 8675309, chore_done: false, created_at: "2012-11-05 01:53:33", updated_at: "2012-11-05 01:53:33"> 
1.9.3-p194 :046 > user.chore.name 
    Chore Load (0.3ms) SELECT "chores".* FROM "chores" WHERE "chores"."user_id" = 4 LIMIT 1 
=> "Living room" 

Czytałem trochę o AssociationProxy w APIDock, ale nie sądzę, rozumiem zupełnie, ani nie wiem jak z nim pracować. Z tego co zebrałem, dostęp do wszystkich obiektów w modelu zwraca tablicę obiektów, ale nie zwraca kompletnego zestawu atrybutów dla jej zależności? Tak więc w tym przypadku dostaję wskaźnik do obiektu Chore, ale nie mam dostępu do żadnego z jego atrybutów.

Mogę z pewnością dodać Chore'a jako kolejną kolumnę w mojej tabeli użytkowników, ale mogę dodać do tego modelu w przyszłości + Chcę tylko dowiedzieć się, jak działa stowarzyszenie has_one.

Każda pomoc jest doceniana!

Odpowiedz

3

undefined method `name' for nil:NilClass

Ten mówi, że starają się wywołać jakąś metodę name() na wystąpienie Nil. W ciągu kontekście kodzie jest, że mówi, że chore na tej linii jest nil

<td><%= user.chore.name %></td> 

Po prostu, jeden z User przypadkach w @user ma powiązaną Chore. Jednym ze sposobów na sprawdzenie tego w swoim widoku jest sprawdzenie, czy istnieje user.chore.

<td><%= user.chore.nil? ? "some default name" : user.chore.name %></td> 
+0

Ah! To ma taki sens. Zrobiłem to znacznie bardziej skomplikowanym niż było. Właśnie zepsuję to, co napisałeś ... Nie widziałem już tej składni (drugiego znaku zapytania) w szynach, ale wydaje się bardzo podobne do zapytań SQL. Chłodny! – jasdeepg

+0

To się nazywa ** operator trójskładnikowy **. '(warunek)? "true case": "false case" ' – deefour

+3

Stylowy sposób na uniknięcie potrójnych połączeń z szynami:' user.chore.try (: name) 'http://apidock.com/rails/Object/try –

5

To dlatego, że nie każdy użytkownik ma związane z nim obowiązki.

Istnieje kilka sposobów na rozwiązanie tego problemu; Jestem częściowy dla wzorca iteracji, używając Array() do wymuszania kolekcji. Można to zrobić w widoku:

<% Array(user.chore).each do |chore| %> 
    <td><%= chore.name %></td> 
    <td><%= chore.done? %></td> 
<% end %> 

Array() będzie instancję pustą tablicę, jeśli chore wynosi zero, czyli nic się nie dzieje podczas próby iteracyjne nad nim. Jeśli występuje usterka, będzie on iterować ponad [chore].

Zaletą tego wzoru jest to, że nie narusza ono Tell don't ask.Pytanie o obiekt, jeśli jest to nil?, jest również możliwe, ale jest to raczej technika proceduralna, która nie wykorzystuje silnych funkcji obiektowych Ruby.

Edit

Jak Deefour zwraca uwagę, spowoduje to w tabeli z kolumnami niedopasowane. Powinieneś prawdopodobnie użyć jego rozwiązania dla tego przypadku.

+0

To doprowadzi do ładny, zniekształcony stół – deefour

+0

Czy możesz opracować? –

+0

Ach, masz rację. Przepraszam, nie zwracam uwagi na resztę stołu. –

4

Oto kolejna opcja, która wykorzystuje moduł Delegowanie szyn:

class User < ActiveRecord::Base 
    has_one :chore 
    delegate :name, to: :chore, prefix: true, allow_nil: true 
end 

To pozwoli Ci zadzwonić <%= user.chore_name %>, który przejdzie rozmowę name na chore, wracając nil jeśli nie ma.

+0

': allow_nil - jeśli ustawione na true, zapobiega wywoływaniu NoMethodError' –

0

Inna opcja:

<td><%= user.chore.try(:name) %></td> 

ten powróci nil jeśli nie ma user.chore.

Powiązane problemy