プログラミングスクールに通いながらオリジナルのWEBアプリを作っています。
そんな中でこれまで直面した問題・課題・エラーメッセージを解決してきたのでそれをまとめています。もし私と同じ事象にあっているかたの参考になれば幸いです(レベルが低すぎてならないと思うけど‥)
password_digest:が暗号化されない問題
userモデルを作成するときにpassword_digestを使って以下のように生成しています。
$ rails g model User nickname:string email:string password_digest:string
またuser.rbファイルの構成を以下のように実装。has_secure_passowordを入れました。
class User < ApplicationRecord before_save { self.email.downcase! } validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }, uniqueness: { case_sensitive: false } has_secure_password end
そしてパスワード暗号化のためのGemをインストール。
Gemfileの以下の部分のコメントを外してから
# Use ActiveModel has_secure_password gem 'bcrypt', '~> 3.1.7'
$ bundle install
自前でrails cでユーザーを作成してみました。
user = User.new(nickname: ‘やまさん’, email: ‘yama123zsensi@yahoo.co.jp’, password_digest: ‘yama’)
user.save
これのpassword_digestのカラムを見ると、暗号化されていません。
問題箇所はココ
user = User.new(nickname: ‘やまさん’, email: ‘yama123zsensi@yahoo.co.jp’, password_digest: ‘yama’)
ユーザーを生成するときに、password:って書かないと暗号化されない。直接password_digestに入れちゃうと駄目。
Validation failed: Kuchikomis is invalidが出る
ショップの口コミ投稿サイトを作っています。
なので
shop は has_many :kuchikomisだし、
kuchikomiは belongs_to :shop
です。
口コミが投稿された時にshop#createアクションの中で処理をしています。
(口コミなんだったらkuchikomi#createじゃないの?という話もありますが、僕の仕様はそうなんです。。)
この中でshopがDBに無かったら保存し、かつkuchikomiも保存する処理です。
で、このようにPOSTで送られてきたデータからインスタンスを生成しています。
@shop = Shop.new(shop_params) @kuchikomi = @shop.kuchikomi.build(kuchikomi_params)
build ←これが不味かった。
この時点ではshopはまだDBに無いためshop.idはありません。
それをbuildを使って関連付けしながらkuchikomiインスタンスを生成しています。
buildはshopがDBに無いうちは使っちゃ駄目なのでした。
なので一旦これでいけました。
@shop = Shop.new(shop_params) @kuchikomi = kuchikomi.new(kuchikomi_params)
Validation failed: User must exist
こんどはこのエラー。
店舗の口コミサイトを作ってますが、ここに当然ユーザーも絡んできます。
ユーザーによって店舗に口コミが投稿されるからです。
userモデルは、has_many :kuchikomisですし、
kuchikomiモデルは、belongs_to :user
となっているのです。
このエラーはkuchikomi.saveで、口コミをDBに保存するタイミングで出ています。
なのでモデル周りの実装が悪いんだろうなと思っていました。
色々調べて回ったら外部キーにnilを許可してないことから起こっているようです。
結論、これでいけました。
kuchikomiモデルで、
belongs_to :user, optional: true
このようにoptional: trueを追記したら大丈夫でした。
参考:https://qiita.com/iguchi1124/items/218e35a145f372062ea4
必ず外部キーが存在しなきゃいけない場合は
belongs_to :user, required: ture
nilがある場合は
belongs_to :user, optional: true
とすると良い、ということですね。
no implicit conversion of Symbol into Integer
これはハッシュを生成して、それをビューで表示させようと思ったら出たエラーです。
簡単に書くと以下のようなことをやっていました。
コントローラーで、
@tests= {} #ハッシュの初期化 @tests[0] = { :user_id => 'uid1', :nickname => 'もぐもぐ', :image => '画像', :comment => 'おいしい'} @tests[1] = { :user_id => 'uid2', :nickname => 'もぐもぐ2', :image => '画2像', :comment => 'おいしい2'}
とやって、ビューで
<% @tests.each do |test| %> <%= test[:nickname] %> <% end %>
とやると上記のエラーが出ます。
翻訳すると「暗黙的にシンボルを整数に変換しない」らしいのですが意味不明です。
あれこれ試していたら最初の@testsを初期化するところで以下のようにしたらエラーが出ないことがわかりました。
@tests = []
すごくもやもやします(;・∀・)
ここで次のように検索して調査を開始。
Rails 2次元 ハッシュ
Rails ハッシュの初期化は必須なの?
調べてわかったことは、まずRailsではハッシュは必ず初期化して使わないといけない、ということです。
ハッシュの初期化は以下の文が一般的です。
hash = {}
これは僕も知ってたからこれを使ったのですが、エラーが出ちゃったんです。次に2次元ハッシュと3次元ハッシュの初期化のやり方を探していました。
#二次元ハッシュの初期化 定型文 hash = Hash.new { |h,i| h[i] = {} } #三次元以上のハッシュの初期化 hash = Hash.new { |h,k| h[k] = Hash.new(&h.default_proc) }
このようになるらしいです。
よく見ると僕の書き方が適合しない気がします。それで「配列の中のハッシュの初期化」で検索したら答えがわかりました。
参照→https://qiita.com/sonoshou/items/bb1f6ef756041b58a5be
ary = [{},{},{},{},{}] こんなことをやるには ary = Array.new(5){{}} => ary = [{},{},{},{},{}] ary[0][:hoge] = "test" => "test" ary => [{:hoge=>"test"}, {}, {}, {}, {}]
このようにする必要があります。
上の僕の例だと
@tests= Array.new(2){{}} #ハッシュの初期化 @tests[0] = { :user_id => 'uid1', :nickname => 'もぐもぐ', :image => '画像', :comment => 'おいしい'} @tests[1] = { :user_id => 'uid2', :nickname => 'もぐもぐ2', :image => '画2像', :comment => 'おいしい2'}
とやって、ビューで
<% @tests.each do |test| %> <%= test[:nickname] %> <% end %>
で出来ました。
本来Array.newの引数の2は、ActiveRecordの件数を取りたかったのでモデル名.lengthかモデル名.countで件数をとって引数に渡しました。
lengthとcountメソッドの違いですが、lengthは配列の長さで、countはSELECT count(*)文を発行しています。
本質的にはcountなんでしょうけど、SQL文を発行するのは遅くなると思うので、lengthを使いたいところです。
と思ってたら、件数が増えてくるとサーバーのメモリの負荷が多くなってくるそうです。そんな中でlengthをするよりは件数カウントするのをDBに任せる(count)にしたほうが良いそうです。
なのでcountでいくことにしました。