before_create抛出NoMethodError?

时间:2013-07-24 18:06:25

标签: ruby-on-rails

架构:

  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:in create_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:in create_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)

2 个答案:

答案 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让你入门。