访问has_one关联的属性

jas*_*epg 5 ruby model ruby-on-rails has-one

我对Rails还是很陌生,所以希望这不是一个愚蠢的问题.

我有两个模型:用户和家务.用户has_one杂事,Chore属于User

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
Run Code Online (Sandbox Code Playgroud)

在我的用户索引中,我正在尝试向所有用户显示并显示与他或她相关的杂项.我这样做是通过将User.all传递给视图并使用.each来遍历每个用户:

<% @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 %>
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法访问Chore的name属性.我收到此错误:

nil的未定义方法`name':NilClass

如果我删除.name属性,它只返回一个指向Chore对象的指针.

我有一种感觉,这与将User.all对象传递给视图然后迭代它有关.只需在控制台中访问特定的User对象(例如User.find(1)),然后访问user.chore.name就可以了.

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: "example@dot.com", 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" 
Run Code Online (Sandbox Code Playgroud)

我在APIDock中读了一些关于AssociationProxy的内容,但我认为我并不完全理解它,也不知道如何使用它.根据我收集的内容,访问模型中的所有对象会返回一个对象数组,但是不会为其依赖项返回一组完整的属性?因此,在这种情况下,我得到一个指向Chore对象的指针,但是不能访问它的任何属性.

我当然可以在我的用户表中添加Chore作为另一个列,但我可能会在将来添加到该模型+我只是想弄清楚has_one关联如何工作.

任何帮助表示赞赏!

Zac*_*emp 5

这是因为不是每个用户都有与之相关的杂务.

有几种方法可以解决这个问题; 我偏爱使用Array()强制集合的迭代模式.您可以在视图中执行此操作:

<% Array(user.chore).each do |chore| %>
  <td><%= chore.name %></td>
  <td><%= chore.done? %></td>
<% end %>
Run Code Online (Sandbox Code Playgroud)

Array()如果chore是nil,将实例化一个空数组,这意味着当你尝试迭代它时没有任何反应.如果存在杂务,它将迭代[chore].

这种模式的好处是它不违反Tell不要求.询问对象是否nil?也是可能的,但它更多的是一种不利用Ruby强大的面向对象特性的过程技术.

编辑

正如Deefour指出的那样,这将导致列表中列不匹配.您应该在这种情况下使用他的解决方案.


Zac*_*emp 5

这是使用Rails的Delegate模块的另一个选项:

class User < ActiveRecord::Base
  has_one :chore
  delegate :name, to: :chore, prefix: true, allow_nil: true
end
Run Code Online (Sandbox Code Playgroud)

这将允许你调用<%= user.chore_name %>,它会将name调用传递给家务,nil如果没有则返回.


dee*_*our 3

nil:NilClass 的未定义方法“名称”

name()这表示您正在尝试在 的实例上调用某些方法Nil。在您的代码上下文中,这表示chore这一行是nil

<td><%= user.chore.name %></td>
Run Code Online (Sandbox Code Playgroud)

简而言之,User中的实例之一@user没有关联的Chore. 在您看来适应这一点的一种简单方法是检查是否user.chore存在。

<td><%= user.chore.nil? ?  "some default name" : user.chore.name %></td>
Run Code Online (Sandbox Code Playgroud)

  • 使用 Rails 避免三元组的时尚方法:`user.chore.try(:name)` http://apidock.com/rails/Object/try (4认同)