6

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

我有两个模型:用户和杂项。User has_one chore,而 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

在我的用户索引中,我试图显示所有用户并显示与他或她相关的家务。我通过将 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 %>

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

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

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

我感觉这与将 User.all 对象传递给视图然后对其进行迭代有关。只需在控制台中访问特定的用户对象(例如 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" 

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

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

任何帮助表示赞赏!

4

4 回答 4

5

这是因为并非每个用户都有与之相关的家务活。

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

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

Array()如果为 nil,将实例化一个空数组chore,这意味着当您尝试迭代它时不会发生任何事情。如果存在杂务,它将遍历[chore].

这种模式的好处是它不违反Tell don't ask。询问一个对象是否nil?也是可能的,但它更多的是一种过程技术,没有利用 Ruby 强大的面向对象特性。

编辑

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

于 2012-11-05T02:01:48.993 回答
5

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

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

这将允许您调用<%= user.chore_name %>,这会将name调用传递给 chore,nil如果没有则返回。

于 2012-11-05T05:24:31.817 回答
3

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

这表示您正在尝试name()Nil. 在您的代码上下文中,这表明chore在这一行是nil

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

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

<td><%= user.chore.nil? ?  "some default name" : user.chore.name %></td>
于 2012-11-05T02:01:39.120 回答
0

另外一个选项:

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

nil如果没有,这将返回user.chore

于 2016-12-31T17:12:01.870 回答