2015-04-22 2 views
1

У меня возникает ошибка при попытке сохранить объект модели в рельсах. Позвольте мне сказать, что я не использовал миграции баз данных и использую ранее существовавшую базу данных с рельсами. Вот мой класс модели:Rails ActiveRecord save error undefined method `[] 'for nil: NilClass

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 

Ошибка возникает на «профиле. сохранить ". Вот соответствующий StackTrace:

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' 

Ошибка говорит: "неопределенный метод` []»для ноль: NilClass"

+1

Там нет локальной переменной с именем '' params' в register'. При этом локальная переменная 'params' инициализируется на' nil' при первом появлении; 'params [: id]' здесь эквивалентно 'nil [: id]', вызывая ошибку, которую вы получили. – mudasobwa

+0

Пройдите через это в консоли: установите параметры, скопировав их из своего журнала и пройдите по строкам кода. Вы должны уметь видеть, что не так. –

+3

ouch, вы делаете много неправильного материала: нет необходимости в attr_accessors, когда его столбцы таблицы, никогда не переопределяйте инициализацию – apneadiving

ответ

1
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' }) 

Btw, ваш метод hash_rep не то, что полезно, попробуйте:

profile = Profile.build({ username: 'foo' }) 
profile.attributes 

Sidenotes:

  • так вы следуете соглашениям, вам не нужно добавлять OSE линии, вы можете просто удалить их: self.table_name = 'profiles', self.primary_key = 'id'

  • остерегайтесь хэшей, кажется, вы не заботиться о строковых или символьных клавиш, но они не являются такими же

  • есть более элегантный способ написать свой методы, но я держал это просто потому, что его не нужно разработать на данном этапе

+0

Спасибо, что это сработало для меня. –

0

В вашем новом методе, вы должны изменить их, чтобы быть символами, из:

@first_name = attributes[first_name] 
@last_name = attributes[last_name] 
@location = attributes[location] 

To:

@first_name = attributes[:first_name] 
@last_name = attributes[:last_name] 
@location = attributes[:location] 

Кроме того, вам не нужно проходить в хеш опций? Как вы его не используете.

0

гораздо лучше установить свойства по умолчанию в рельсах через обратные вызовы:

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, так как у активной записи есть куча.

Ваш контроллер также не имеет метки. 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 
+0

Я не согласен, обратные вызовы - это чума. Если вам нужны значения по умолчанию при создании чего-то, вы просто используете построитель. Его часть хорошо известных и используемых шаблонов дизайна – apneadiving

+0

Имея тонны кода назначения шаблона, болтающиеся на всем протяжении, звучит как действительно хорошая идея - нет. – max

+0

правда, есть лучшие способы, но вы отвечаете новичкам. Поэтому нет нужды путать его прямо сейчас. BTW, он может использовать ваш код, не получая исключения: 'password is not a attribute' – apneadiving

Смежные вопросы