这个错误让我困扰了好几个星期。当我尝试从指定的Item
和User
构建List
时,会创建Item
,但关联Wish
不会。
如果我尝试@item.lists.first.name
,则会返回错误:
undefined method 'name' for nil:NilClass
我对rails很新,所以我确信有些东西我错过了或被误解了。因此,非常感谢任何帮助!
我有四种模式:
class User < ActiveRecord::Base
has_many :lists, dependent: :destroy
has_many :wishes, through: :lists
has_many :items, through: :wishes
class List < ActiveRecord::Base
belongs_to :user
has_many :wishes, dependent: :destroy
has_many :items, through: :wishes
class Wish < ActiveRecord::Base
belongs_to :list
belongs_to :item
class Item < ActiveRecord::Base
has_many :wishes
has_many :lists, through: :wishes
我想从Item
show动作中创建一个新的lists_controller.rb
:
class ListsController < ApplicationController
def show
@user = User.find(params[:user_id])
@list = @user.lists.find(params[:id])
@item = @list.items.build if current_user?(@user)
@items = @list.items
end
class ItemsController < ApplicationController
def create
@list = current_user.lists.find(params[:list_id])
@item = @list.items.build(params[:item])
if @item.save
flash[:success] = "Item created!"
redirect_to @item
else
render 'new'
end
end
我的路线文件如下:
Wishlist::Application.routes.draw do
resources :users do
resources :lists, only: [:show, :create, :destroy]
end
resources :items, only: [:show, :new, :create, :destroy]
表单lists/show.html.erb
:
<div class="modal-body">
<%= form_for(@item, html: { multipart: true }) do |f| %>
<div class="field">
<%= render 'items/fields', f: f %>
<%= hidden_field_tag :list_id, @list.id %>
</div>
</div>
和items/fields
:
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :image %>
<%= f.file_field :image %>
<%= f.label :remote_image_url, "or image URL" %>
<%= f.text_field :remote_image_url %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :link %>
<%= f.text_field :link %>
更新
来自日志:
Processing by ItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"pcDVdaDzZz4M17Kwjx8mKw6tTF9MjUvx1woTzaKRWJY=", "item"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x007fecdd1fd890 @original_filename="profile.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"item[image]\"; filename=\"profile.jpg\"\r\nContent-Type: image/jpeg\r\n", @tempfile=#<File:/var/folders/6y/j8zfcgmd02x5s439c0np8fjh0000gn/T/RackMultipart20130216-8413-3vzjuj>>, "remote_image_url"=>"", "title"=>"YES", "link"=>"SDFs"}, "list_id"=>"1", "commit"=>"Add"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '5AXS8-7-YRyLyGDKYHIZRg' LIMIT 1
List Load (0.2ms) SELECT "lists".* FROM "lists" WHERE "lists"."user_id" = 1 AND "lists"."id" = ? LIMIT 1 [["id", "1"]]
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "items" ("created_at", "image", "link", "title", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Sat, 16 Feb 2013 20:57:10 UTC +00:00], ["image", "profile.jpg"], ["link", "SDFs"], ["title", "YES"], ["updated_at", Sat, 16 Feb 2013 20:57:10 UTC +00:00]]
(2.7ms) commit transaction
Redirected to http://localhost:3000/items/1
Completed 302 Found in 830ms (ActiveRecord: 4.0ms)
答案 0 :(得分:0)
让我首先尝试总结一下:你有用户,其中包含与一个特定项目相关的愿望的列表。
首先让我们先看看以下代码行:
@item.lists.first.name
所以会发生的是,rails获取该项目,遍历所有包含此项目的列表的愿望,获取第一个列表并查找列表名称。当项目与愿望中的任何列表无关时,您会看到问题(例如,因为您的项目创建方法没有将新项目连接到愿望,因此也缺少与列表的连接):所以项目没有关系到列表,@item.lists
的结果是一个空列表。好吧,空列表的first
元素没有任何内容(nil
),当然nil
没有名称。
结果是,你看到了什么:
undefined method 'name' for nil:NilClass
解决此问题的一种方法是使用try()
,只有在相关方法可用时才执行相关方法:
@item.lists.first.try(:name)
您正在谈论的另一个问题是,当您从用户构建项目时,不会产生任何愿望。我认为你必须明确地建立愿望。我不是百分百肯定,但我认为,声明:through
- 模型中的关系仅用于查询,而不用于生成/构建。
在你的项目控制器中尝试类似下面的内容(想法是首先明确地构建愿望,然后在构建愿望的项目之后):
@wish = @list.wishes.build()
@wish.save
@item = @wish.item.build(params[:item])
答案 1 :(得分:0)
在build
上使用has_many :through
时,这似乎是一个已知问题(但不是真正的问题)。您需要更改items
上List
的关联,以包含inverse_of
。
class List < ActiveRecord::Base
belongs_to :user
has_many :wishes, dependent: :destroy
has_many :items, through: :wishes, inverse_of: :wishes
end
然后应该为您正确构建连接Wish
对象。
有关详细信息,请参阅此相关问题:https://github.com/rails/rails/pull/7661#issuecomment-8614206
答案 2 :(得分:0)
经过大量的谷歌搜索和反复试验,我发现我必须明确地建立愿望。创建操作最终看起来像这样:
def create
@list = current_user.lists.find(params[:list_id])
@item = Item.create!(params[:item])
@item.wishes.build(list_id: @list.id)
if @item.save
flash[:success] = "Item created!"
redirect_to @item
else
render 'new'
end
end