Rails ActiveRecord save error undefined method `[]' for nil:NilClass
尝试将我的模型对象保存在 Rails 中时出现错误。让我说我没有使用数据库迁移,而是使用带有 Rails 的预先存在的数据库。
这是我的模型类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | require 'bcrypt' require 'securerandom' class Profile < ActiveRecord::Base include BCrypt self.table_name = 'profiles' self.primary_key = 'id' attr_accessor :id, :username, :password_hash, :salt, :first_name, :last_name, :location, :status, :game_status def initialize(attributes = {}, options = {}) @username = attributes[:username] @salt=SecureRandom.hex @password_hash = Password.create(attributes[:password] + @salt).to_s @first_name = attributes[first_name] @last_name = attributes[last_name] @location = attributes[location] @status ="Hi" @game_status ="Playing some game..." end def hash_rep hash = {} hash['id'] = @id hash['username'] = @username hash['password_hash'] = @password_hash hash['salt'] = @salt hash['location'] = @location hash['status'] = @status hash['game_status'] = @game_status return hash end end |
这是我的数据库架构:
1 2 3 4 5 6 7 8 9 | id int Unsigned NOT NULL AUTO_INCREMENT username varchar(16) NOT NULL password_hash tinytext NOT NULL salt varchar(64) NOT NULL first_name varchar(16) NOT NULL last_name varchar(16) NOT NULL location tinytext NOT NULL status tinytext NULL game_status tinytext NULL |
这是我的控制器代码:
1 2 3 4 5 6 7 8 9 10 | def register profile = Profile.new(:id => params[:id], :username => params[:username], :password => params[:password], :first_name => params[:first_name], :last_name => params[:last_name], :location => params[:location]) profile.save render_profile(profile) end |
错误发生在 \\'profile.save\\' 方法上。这是相关的堆栈跟踪:
1 2 3 4 5 6 7 | activerecord (4.2.0) lib/active_record/transactions.rb:375:in `clear_transaction_record_state' activerecord (4.2.0) lib/active_record/transactions.rb:306:in `ensure in rollback_active_record_state!' activerecord (4.2.0) lib/active_record/transactions.rb:306:in `rollback_active_record_state!' activerecord (4.2.0) lib/active_record/transactions.rb:285:in `save' app/controllers/profile_controller.rb:52:in `register' actionpack (4.2.0) lib/action_controller/metal/implicit_render.rb:4:in `send_action' actionpack (4.2.0) lib/abstract_controller/base.rb:198:in `process_action' |
错误提示:"undefined method `[]\\' for nil:NilClass"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | require 'bcrypt' require 'securerandom' class Profile < ActiveRecord::Base include BCrypt self.table_name = 'profiles' self.primary_key = 'id' def hash_rep hash = {} hash['id'] = id hash['username'] = username hash['password_hash'] = password_hash hash['salt'] = salt hash['location'] = location hash['status'] = status hash['game_status'] = game_status hash end def self.build(args) new_profile = Profile.new new_profile.username = args[:username] salt=SecureRandom.hex new_profile.password_hash = Password.create(args[:password] + salt).to_s new_profile.first_name = args[:first_name] new_profile.last_name = args[:last_name] new_profile.location = args[:location] new_profile.status ="Hi" new_profile.game_status ="Playing some game..." new_profile end end |
现在你可以像这样使用它:
1 | Profile.build({ username: 'foo' }) |
顺便说一句,你的 hash_rep 方法没那么有用,试试:
1 2 | profile = Profile.build({ username: 'foo' }) profile.attributes |
旁注:
-
由于你遵循约定,你不需要添加这些行,你可以删除它们:
self.table_name = 'profiles' ,self.primary_key = 'id' -
小心散列,似乎你不关心字符串或符号键,但它们不一样
-
有更优雅的方法来编写你的方法,但我保持简单,因为在这个阶段没有必要详细说明
在 Rails 中设置默认属性的更好方法是通过回调:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | require 'bcrypt' require 'securerandom' class Profile < ActiveRecord::Base include BCrypt attr_accessor :password # is a virtual attribute after_initialize do if new_record? # values will be available for new record forms. self.status = status ||"Hi" self.game_status = game_status ||"Playing some game..." end end before_validation(on: :create) do self.salt=SecureRandom.hex self.password_hash = Password.create(password + salt).to_s end # Yuck. use http://apidock.com/rails/ActiveModel/Serialization/serializable_hash def hash_rep serializable_hash( only: [:id, :username, :password_hash, :salt, :location, :status, :game_status] ) end end |
您不需要为 ActiveRecord 列创建访问器。您不需要指定
您的控制器也缺少标记。 Rails 通常在资源名称下嵌套参数。如果您使用的是 Rails 4 - 您将通过以下方式将参数列入白名单并分配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class ProfileController < ApplicationController def new @profile = Profile.new end def register @profile = Profile.new(create_params) if @profile.save redirect_to @profile else render action: :new end end private def create_params params.require(:profile).allow(:username, :password, : first_name :last_name :location) end end |
在您的新方法中,您应该将这些更改为符号,来自:
1 2 3 | @first_name = attributes[first_name] @last_name = attributes[last_name] @location = attributes[location] |
收件人:
1 2 3 | @first_name = attributes[:first_name] @last_name = attributes[:last_name] @location = attributes[:location] |
另外,您不需要传递选项哈希吗?因为你不使用它。