2012-11-05 13 views
5

Ich bin immer noch ziemlich neu bei Rails, also ist das hoffentlich keine dumme Frage.Zugriff auf Attribute von has_one associations

Ich habe zwei Modelle: User und Chore. Benutzer has_one lästige Pflicht, und Chore gehört Benutzer

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 

In meinem Benutzer-Index, ich versuche, alle Benutzer zu zeigen und die lästige Pflicht an, die mit ihm oder ihr zugeordnet ist. Ich tue dies durch User.all zur Ansicht vorbei und ein .each Verwendung durch jeden Benutzer iterieren:

<% @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 %> 

Leider kann ich nicht den Namen Attribut des Chore zugreifen. Ich bekomme diese Fehlermeldung:

undefined method `name' for nil:NilClass

Wenn ich das .name Attribut zu entfernen, es gibt nur einen Zeiger auf das Objekt Chore.

Ich habe das Gefühl, dies hat etwas damit zu tun, das User.all-Objekt an die Ansicht zu übergeben und dann darüber zu iterieren. Der Zugriff auf ein bestimmtes Benutzerobjekt (z. B. User.find (1)) in der Konsole und der Zugriff auf user.chore.name funktioniert problemlos.

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" 

las ich ein wenig über AssociationProxy im APIDock, aber ich glaube nicht, dass ich es vollständig zu verstehen, noch weiß ich, wie man mit ihm zu arbeiten. Nach allem, was ich gesammelt habe, gibt der Zugriff auf alle Objekte in einem Modell ein Array von Objekten zurück, gibt aber keinen vollständigen Satz von Attributen für seine Abhängigkeiten zurück. In diesem Fall erhalte ich einen Zeiger auf das Chore-Objekt, aber keinen Zugriff auf eines seiner Attribute.

Ich könnte sicherlich einfach Chore als eine weitere Spalte in meiner Benutzertabelle hinzufügen, aber ich kann zu diesem Modell in der Zukunft hinzufügen + Ich möchte nur herausfinden, wie die has_one Verknüpfung trotzdem funktioniert.

Jede Hilfe wird geschätzt!

Antwort

3

undefined method `name' for nil:NilClass

Diese besagt, dass Sie eine Methode name() auf eine Instanz von Nil anrufen möchten. Im Rahmen Ihres Codes, die besagt, dass chore auf dieser Linie ist nil

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

Einfach gesagt, eine der User Instanzen in @user hat keine zugehörigen Chore. Eine einfache Möglichkeit, dies in Ihrer Ansicht zu berücksichtigen, besteht darin, zu überprüfen, ob user.chore existiert.

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

Ah! Das macht so viel Sinn. Ich machte das viel komplizierter als es war. Ich zerlege nur, was du geschrieben hast ... Ich habe diese Syntax (das zweite Fragezeichen) in Schienen noch nicht gesehen, aber sie scheint SQL-Abfragen sehr ähnlich zu sein. Cool! – jasdeepg

+0

Es heißt ein ternärer Operator **. '(Zustand)? "wahrer Fall": "falscher Fall" ' – deefour

+3

Stilvoller Weg, ternaries mit Schienen zu vermeiden:' user.chore.try (: Name) 'http://apidock.com/rails/Object/try –

5

Es ist, weil nicht jeder Benutzer eine Arbeit damit verbunden ist.

Es gibt ein paar Möglichkeiten, wie Sie damit umgehen können; Ich bin Teil des Iterationsmusters mit Array(), um eine Sammlung zu erzwingen. Sie können dies Ihrer Ansicht nach tun:

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

Array() wird ein leeres Array instanziiert wenn chore null ist, nichts bedeutet passiert, wenn Sie versuchen, über sie zu wiederholen. Wenn ein Job vorhanden ist, wird über [chore] iteriert.

Der Vorteil dieses Musters ist, dass es Tell don't ask nicht verletzt.Ein Objekt zu fragen, wenn es nil? ist, ist auch möglich, aber es ist eher eine prozedurale Technik, die Rubys starke objektorientierte Eigenschaften nicht ausnutzt.

bearbeiten

Wie Deefour darauf hinweist, wird dies mit nicht übereinstimmen Spalten in einer Tabelle führen. Sie sollten wahrscheinlich seine Lösung für diesen Fall verwenden.

+0

Das wird in a resultieren ziemlich gemangelt Tisch – deefour

+0

Kannst du es ausarbeiten? –

+0

Ah, du hast Recht. Entschuldigung, ich werde auf den Rest des Tisches achten. –

4

Hier ist eine andere Option, die Rails' Delegate-Modul verwendet:

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

Dies ermöglicht es Ihnen <%= user.chore_name %> zu nennen, die die name Anruf auf lästige Pflicht passieren, Rückkehr nil wenn es keine gibt.

+0

': allow_nil - wenn der Wert auf 'true' gesetzt ist, wird verhindert, dass NoMethodError ausgelöst wird. ' –

0

Eine weitere Option:

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

Dies wird nil zurück, wenn es keine user.chore ist.