关于 ruby??:Rails ActiveRecord save error undefined method `[]’ for nil:NilClass

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 列创建访问器。您不需要指定 tableprimary_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
    @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]

另外,您不需要传递选项哈希吗?因为你不使用它。