0

schema:

  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

models:

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

So I tried creating a star in the Rails console like this:

post = Post.first
user = User.first
star = post.stars.build(number: 1)
star.user_id = user.id

And everything goes OK 'till here. But when I try to save it:

star.save

I get this error:

NoMethodError: undefined method +' for nil:NilClass from /home/alex/rails/rating/app/models/star.rb:10:inadd_to_total_stars' from /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' from /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:increate_record' from /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:inblock in create_or_update' from /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'

What could be the cause?

(I'm using Rails 4)

4

2 回答 2

1

您收到该错误是因为starable.total_stars您的回调方法中为 nil 。您需要确保将starable.total_stars其设置为 0 ro 您可以对其调用 to_i 方法(nil.to_i #=> 0)以确保如果未初始化则为 0

于 2013-07-24T18:31:58.687 回答
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 截屏视频。

于 2013-07-24T18:47:40.180 回答