Mysql2::Error: Field '****' doesn't have a default valueに二回もハマった話


Mysql2::Error: Field 'tag_name' doesn't have a default value
とは、NOT NULL制約をかけているにも関わらず値が入っていないよーといった感じのエラーです

class CreateTags < ActiveRecord::Migration[6.0]
  def change
    create_table :tags do |t|
      t.string :name,null:false
      t.timestamps
    end
  end
end

とかでカラムに,null: falseで必ず値が入るように設定していたと思います

なので、rails側でも

validates :name,presence: true

と、バリデーションをかけていたと思います。

というわけで、まず考えられる原因が

マイグレーションファイルでnull: falseとNOT NULL制約を設けていたにも関わらず、Rails側でバリデーション をかけていなくて、form_withとか何とかで、値を保存しようとした!!

といったことが考えられます。

しかし自分は違いました、バリデーション もかけている

[しくじった原因1: ストロングパラメーターの記述が間違ってる]

{"authenticity_token"=>"T8/aOv7fmGDk6UO/GHiYkODYvpMTH/3tFP6sCW0QPVVaAVh6ZHcAd2xaTQzcuioVXdOOWYAFNHO8u3S0OmnG6Q==",
 "drink_tag"=>
  {"tag_name"=>"酸味",
   "image"=>
    #<ActionDispatch::Http::UploadedFile:0x00007f8a24b3d318
     @content_type="image/jpeg",
     @headers="Content-Disposition: form-data; name=\"drink_tag[image]\"; filename=\"ethiopia.jpg\"\r\n" + "Content-Type: image/jpeg\r\n",
     @original_filename="ethiopia.jpg",
     @tempfile=#<File:/var/folders/34/pbcy_n7j1q79hnpm7sbcgl0m0000gn/T/RackMultipart20201204-1452-13ocgpk.jpg>>,
   "name"=>"エチオピア",
   "explain"=>"酸味があってアイスもおすすめ!!",
   "price"=>"1000"},
 "commit"=>"投稿する"}

と、パラメーターがとんでいて

  private
  def drink_params
    params.require(:drink_tag).permit(:name,:price,:explain,:image,:tag_name).merge(user_id: current_user.id)
  end

とストロングパラメーターを書くのが正しいですが

自分の場合、ストロングパラメータくらい分かったつもりになって教材をコピペしたせいで,

  private
  def drink_params
    params.require(:tweet_tag).permit(:name,:price,:explain,:image,:tag_name).merge(user_id: current_user.id)
  end

と、

params.require(:drink_tag)

params.require(:tweet_tag)

にしていました、、、。

ストロングパラメーターが間違っていたら
正しい値を許容できていないので

  def create
    @drink = DrinkTag.new(drink_params)

    if @drink.save
      redirect_to drinks_path
    else
      render 'new'
    end
  end

@drinkは空,nilなので、
nilな物を保存すんな!って怒られてしまいます、、、。
form_withの使い方はあってたので、しっかりとしたパラメーターが作られて
railsのバリデーションの網はかいくぐったようですが、MySQLちゃんがしっかりと値の保存を防いでくれました、、、。

[しくじった原因2: Formオブジェクトを用いた値の保存で、一度に保存するためのmodelの記述が間違ってる!]

Formオブジェクトとは、一つのビューで複数の値を保存したいときに用いられるテクニック?的なものです。
( 詳しくは検索、検索♪)

class UserDonation

  include ActiveModel::Model
  attr_accessor :name, :name_reading, :nickname, :postal_code, :prefecture, :city, :house_number, :building_name, :price

   with_options presence: true do
    validates :name, format: { with: /\A[ぁ-んァ-ン一-龥]/, message: "は全角で入力してください。"}
    validates :name_reading, format: { with: /\A[ァ-ヶー-]+\z/, message: "は全角カタカナで入力して下さい。"}
    validates :nickname, format: { with: /\A[a-z0-9]+\z/i, message: "は半角英数で入力してください。"}
  end

  def save


    user = User.create(name: name, name_reading: name_reading, nickname: nickname)


    Address.create(postal_code: postal_code, prefecture: prefecture, city: city, house_number: house_number, 
    building_name: building_name, user_id: user.id)

    Donation.create(price: price, user_id: user.id)
  end

end

と、一つのビューで複数のテーブルに値を保存したい時に、こういったモデルをよく作りますが

saveメソッドでのキー、バリューの書き忘れ!!!

で自分はつまずきました、、、。

この定義したsaveメソッドはあとでコントローラーで


 def create
   @donation = UserDonation.new(donation_params)

   if @donation.valid?
     @donation.save  # バリデーションをクリアした時
     return redirect_to root_path
   else
     render "new"    # バリデーションに弾かれ時
   end
 end

とsaveメソッドを用いますが、

例えば、

  def save


    user = User.create(name: name, name_reading: name_reading, nickname: nickname)


    Address.create(postal_code: postal_code, prefecture: prefecture, city: city, house_number: house_number, 
    building_name: building_name, user_id: user.id)

    Donation.create(price: price, user_id: user.id)
  end

で、

Address.create(postal_code: postal_code, prefecture: prefecture, city: city, house_number: house_number,
building_name: building_name, user_id: user.id)



postal_code: postal_code

が抜け落ちてたら、このエラーが起きます、、、。
なので、しっかりsaveメソッドが定義できてるかみましょう!!

これもフォームで入力して、しっかりパラメーターが作られるので、railsのバリデーション の
網を抜けたのでしょうが、データベース側でも「nilはだめ!!」としていたので、助かりました、。

 まとめ

バリデーション をかけているにもかかわらず、このエラーが起きるということは、モデルかコントローラーに
何かしらの問題がある。

もっというと、コントローラーは、createで値を保存するので、
ストロングパラメーターに問題があったり、

formオブジェクトはmodelにsaveメソッドを定義してるので、saveメソッドに問題が
ないか確かめましょう、、、!!!

他にもこのエラーに対する考えられる原因と、それの対処法があればコメントお願いします!!