Rails nil:NilClass 的 ActiveRecord 保存错误未定义方法“[]”
Rails ActiveRecord save error undefined method `[]' for nil:NilClass
我在 rails 中尝试保存我的模型对象时遇到错误。让我说我没有使用数据库迁移,而是使用 rails 的预先存在的数据库。
这是我的模型 class:
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
这是我的数据库模式:
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
这是我的控制器代码:
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' 方法上。这是相关的堆栈跟踪:
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"
在您的新方法中,您应该将这些更改为符号,来自:
@first_name = attributes[first_name]
@last_name = attributes[last_name]
@location = attributes[location]
收件人:
@first_name = attributes[:first_name]
@last_name = attributes[:last_name]
@location = attributes[:location]
此外,您不需要传入选项哈希?因为你不用。
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
现在您可以像这样使用它:
Profile.build({ username: 'foo' })
顺便说一句,你的 hash_rep 方法没那么有用,试试:
profile = Profile.build({ username: 'foo' })
profile.attributes
旁注:
因为你遵循约定,你不需要添加这些行,你可以删除它们:self.table_name = 'profiles'
, self.primary_key = 'id'
注意哈希,似乎您不关心字符串或符号键,但它们并不相同
有更优雅的方式来编写您的方法,但我保持简单,因为在这个阶段没有必要详细说明
在 rails 中设置默认属性的更好方法是通过回调:
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 列创建访问器。您不需要指定 table
和 primary_key
。 Rails 为您解决这个问题。另外,您真的不想重新定义 initialize
,因为 active record 在那里进行了很多操作。
您的控制器也没有标记。 Rails 通常在资源名称下嵌套参数。如果您使用 Rails 4 - 您将 whitelist 并通过以下方式分配参数:
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
我在 rails 中尝试保存我的模型对象时遇到错误。让我说我没有使用数据库迁移,而是使用 rails 的预先存在的数据库。 这是我的模型 class:
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
这是我的数据库模式:
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
这是我的控制器代码:
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' 方法上。这是相关的堆栈跟踪:
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"
在您的新方法中,您应该将这些更改为符号,来自:
@first_name = attributes[first_name]
@last_name = attributes[last_name]
@location = attributes[location]
收件人:
@first_name = attributes[:first_name]
@last_name = attributes[:last_name]
@location = attributes[:location]
此外,您不需要传入选项哈希?因为你不用。
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
现在您可以像这样使用它:
Profile.build({ username: 'foo' })
顺便说一句,你的 hash_rep 方法没那么有用,试试:
profile = Profile.build({ username: 'foo' })
profile.attributes
旁注:
因为你遵循约定,你不需要添加这些行,你可以删除它们:
self.table_name = 'profiles'
,self.primary_key = 'id'
注意哈希,似乎您不关心字符串或符号键,但它们并不相同
有更优雅的方式来编写您的方法,但我保持简单,因为在这个阶段没有必要详细说明
在 rails 中设置默认属性的更好方法是通过回调:
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 列创建访问器。您不需要指定 table
和 primary_key
。 Rails 为您解决这个问题。另外,您真的不想重新定义 initialize
,因为 active record 在那里进行了很多操作。
您的控制器也没有标记。 Rails 通常在资源名称下嵌套参数。如果您使用 Rails 4 - 您将 whitelist 并通过以下方式分配参数:
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