架构:
create_table "posts", force: true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "total_stars"
t.integer "average_stars"
end
create_table "stars", force: true do |t|
t.integer "starable_id"
t.string "starable_type"
t.integer "user_id"
t.integer "number"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "stars", ["starable_id", "starable_type"], name: "index_stars_on_starable_id_and_starable_type"
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
模型:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
end
class Star < ActiveRecord::Base
before_create :add_to_total_stars
belongs_to :starable, :polymorphic => true
protected
def add_to_total_stars
if [Post].include?(starable.class)
self.starable.update_column(:total_stars, starable.total_stars + self.number)
end
end
end
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :votes, dependent: :destroy
end
所以我尝试在Rails控制台中创建一个这样的星:
post = Post.first
user = User.first
star = post.stars.build(number: 1)
star.user_id = user.id
一切顺利'直到这里。但是当我试图保存它时:
star.save
我收到此错误:
NoMethodError:未定义的方法
+' for nil:NilClass from /home/alex/rails/rating/app/models/star.rb:10:in
add_to_total_stars' 从 /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:377:in_run__956917800__create__callbacks' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks'来自 /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:303:increate_record' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/timestamp.rb:57:in
create_record'来自 /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/persistence.rb:466:increate_or_update' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:299:in
块中的create_or_update'来自 /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:383:in_run__956917800__save__callbacks' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks'
可能是什么原因?
(我正在使用Rails 4)
答案 0 :(得分:1)
您收到该错误,因为starable.total_stars
在您的回调方法中为零。你需要确保starable.total_stars
设置为0 ro你可以调用它上面的to_i方法(nil.to_i #=> 0
),以确保你没有初始化时为0
答案 1 :(得分:1)
看起来Post.first.total_stars
的值为零。根据您的架构和模型示例,您在数据库中允许null,并且不验证它在ActiveRecord中的存在。
如果将此值默认为0是有意义的,那么您应该在架构中设置默认值。
所以我会将以下内容添加到您的架构中:
create_table "posts", force: true do |t|
# ...
t.integer "total_stars", null: false, default: 0
end
在您的模型中进行以下验证:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
validates :total_stars, presence: true
end
顺便说一下,我会完全摆脱total_stars
并让rails用counter_cache
选项为你做这件事。这里是Railscast screencast让你入门。