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’ attr_accessor :id, :username, :password_hash, :salt, :first_name, :last_name, :location, :status, :game_status def initialize(attributes = {}, options = {}) def hash_rep 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”
- register 中没有名为 params 的局部变量。也就是说,局部变量 params 在第一次出现时被初始化为 nil ; params[:id] 在此等同于 nil[:id] 导致您遇到的错误。
- 在控制台中逐步执行此操作:通过从日志中复制来设置参数并逐行执行代码。您应该能够看到不正确的地方。
- 哎呀,你做错了很多事情:关于表列时不需要 attr_accessors,不要重新定义初始化
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’ def hash_rep def self.build(args) |
现在你可以像这样使用它:
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 before_validation(on: :create) do # Yuck. use http://apidock.com/rails/ActiveModel/Serialization/serializable_hash end |
您不需要为 ActiveRecord 列创建访问器。您不需要指定 table 和 primary_key。 Rails 为您解决了这个问题。此外,您真的不想重新定义 initialize 因为活动记录有很多在那里进行。
您的控制器也缺少标记。 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 def register private |
- 我不同意,回调是一场瘟疫。如果您在构建某些东西时需要默认值,那么您只是在使用构建器。它是众所周知和使用的设计模式的一部分
- 有大量的样板分配代码到处晃荡听起来是个好主意——不是。
- 确实,有更好的方法,但您是在回答初学者。所以现在没必要迷惑他。顺便说一句,他无法使用您的代码而不会出现异常:password is not an attribute
- 编辑添加密码访问器。但实际上他应该使用 has_secure_password 而不是尝试从头开始进行密码摘要。它太容易搞砸了。
- :) 一旦您对这些工具感到满意,就可以逐步进行抽象……
- 顺便说一句,用 before_create 替换你的 before_save 否则它会失败……回调……
- 是的,但在这种情况下,它应该是 before_validation,因为验证发生在 before_create 之前。 api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
- before_validation 发生在每次保存之前(和每次 .valid? 调用之前)
- 你真的应该改变,它会在更新时出错:)
在您的新方法中,您应该将这些更改为符号,来自:
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] |
另外,您不需要传递选项哈希吗?因为你不使用它。
来源:https://www.codenong.com/29791286/