🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

バリデーション

Validationとは特定の入力データが、求められた条件に当てまっているかをチェックするために使われます。

Q&A

解決済

3回答

2011閲覧

[Rails]バリデーションをかけているのですが、効きません。理由と解決方法を知りたいです。

mball

総合スコア7

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

バリデーション

Validationとは特定の入力データが、求められた条件に当てまっているかをチェックするために使われます。

0グッド

0クリップ

投稿2019/11/01 14:54

編集2019/11/01 14:56

前提・実現したいこと

Railsで簡単なイベント閲覧アプリを作成しています。
イベントを登録する際にバリデーションをかけて、以下のエラーが発生したので解決したいのですが、できません。
エラー画面

発生している問題・エラーメッセージ

イベントを登録する際に、以下のバリデーションをかけています。

・イベント名(event_name)が存在すること
・開催地の郵便番号(postcode)が北海道であること
・開催地の住所が存在すること
・登録するイベントの開始日が今日以降であること
・登録するイベントの終了日が開始日以降であること

問題は、

・イベント名(event_name)が存在すること

というバリデーションは有効であるのに対して

・開催地の住所が存在すること

というバリデーションは無効であることです。

undefined method `[]' for nil:NilClass

このエラーメッセージは開催地の住所が空であるにも関わらず、緯度経度を取得しようとしているがゆえに起きるメッセージです。
「開催地の住所が存在すること」というバリデーションが無効という判断は、上記エラーより推測しました。

該当のソースコード

以下はモデル(event.rb)のコードです。

ruby

1class Event < ApplicationRecord 2 has_many :interests 3 has_many :interested_users, through: :interests, source: :user 4 has_many :likes 5 has_many :liked_users, through: :likes, source: :user 6 belongs_to :category 7 8 with_options presence: true do 9 validates :event_name 10 validates :place 11 end 12 validates :postcode, format: {with: /\A[0-9]{5,6}\z/} 13 validates :postcode, format: {without: /\A[1-3][0-9]{5}\z/} 14 validate :not_before_today 15 validate :not_before_start_on 16 after_validation :geocode 17 18 def not_before_today 19 errors.add(:start_on, 'Please set today or after today') if start_on.nil? || start_on < Date.today 20 end 21 22 def not_before_start_on 23 errors.add(:end_on, 'Please set after start day') if end_on.nil? || end_on < start_on 24 end 25 26 # ↓↓住所から緯度経度を取得してDBに保存するための記述↓↓ 27 private 28 def geocode 29 address = (place + place_building) 30 uri = URI.escape("https://maps.googleapis.com/maps/api/geocode/json?address="+address.gsub(" ", "")+"&key=AIzaSyCuZz7CQo9VlX7NCAFAPiNevqd7NTWpXO8") 31 res = HTTP.get(uri).to_s 32 response = JSON.parse(res) 33 self.latitude = response["results"][0]["geometry"]["location"]["lat"] 34 self.longitude = response["results"][0]["geometry"]["location"]["lng"] 35 end 36 37end

関連するコントローラ(events_controller.rb)は以下のコードです。

ruby

1class EventsController < ApplicationController 2 before_action :authenticate_user!, only: [:show, :new, :create] 3 4 def index 5 @search = Event.ransack(params[:q]) #ransackメソッド推奨 6 @events = @search.result.includes(:category).order(:start_on) 7 @allevents = Event.where('start_on >= ?', Date.today).order(:start_on) 8 end 9 10 def new 11 @event = Event.new 12 end 13 14 def create 15 @event = Event.new(event_params) 16 17 if @event.save 18 redirect_to action: 'index' 19 else 20 render "new" 21 end 22 23 end 24 25 def show 26 @event = Event.find(params[:id]) 27 @interest = Interest.new 28 @like = Like.new 29 end 30 31 private 32 def event_params 33 params.require(:event).permit(:event_name, :url, :start_on, :end_on, :category_id, :postcode, :place, :place_building, :latitude, :longitude) 34 end 35end 36

投稿フォームのビュー(new.html.erb)は以下の通りです

ruby

1<div class="main-wrapper"> 2 3 <h3>イベント登録する</h3> 4 5 <div class="form-wrapper"> 6 7 <%= form_for @event do |f| %> 8 9 <%= render 'layouts/error_messages', model: f.object %> 10 11 <div class="event_name"> 12 <%= f.label :event_name, "イベント名 (必須)" %> 13 <%= f.text_field :event_name %> 14 </div> 15 16 <div class="postcode"> 17 <%= f.label :postcode, "郵便番号 (必須)" %> 18 <%= f.text_field :postcode, size: "10", maxlength: "8", onKeyUp: "AjaxZip3.zip2addr(this,'','addr11','addr11');", placeholder: "ハイフンなし7桁" %> <%# name属性のzip11は無くてもいい %> 19 </div> 20 21 <div class="place"> 22 <div class="place__address"> 23 <%= f.label :place, "住所" %> 24 <%= f.text_field :place, name: "addr11", size: "60", class: "false-place", placeholder: "郵便番号から自動入力されます" %> 25 </div> 26 <div class="place__building"> 27 <%= f.label :place_building, "建物名等" %> 28 <%= f.text_field :place_building, placeholder: "あったら助かります" %> 29 </div> 30 </div> 31 32 <div class="url"> 33 <%= f.label :url, "イベントURL" %> 34 <%= f.text_field :url, class:'form-control', placeholder: "例: https://hokkaido.com" %> 35 </div> 36 37 <div class="start_on"> 38 <div class="start_on__label"> 39 <%= f.label :start_on, "開始日" %> 40 </div> 41 <%= f.date_field :start_on, value: Time.now.strftime("%Y-%m-%d"), class: 'form-control' %> 42 </div> 43 44 <div class="end_on"> 45 <div class="end_on__label"> 46 <%= f.label :end_on, "終了日" %> 47 </div> 48 <%= f.date_field :end_on, value: Time.now.strftime("%Y-%m-%d"), class: 'form-control' %> 49 </div> 50 51 <div class="category clearfix"> 52 <div class="category__label"> 53 <%= f.label :category_id, "カテゴリー"%> 54 </div> 55 <%= f.select :category_id, [["グルメ",1], ["お祭り",2], ["花火",3], ["よさこい",4], ["その他",5]] %> 56 </div> 57 58 <%= f.submit '登録', class:'btn btn-primary add-btn' %> 59 60 <% end %> 61 62 </div> 63 64</div>

投稿フォームのビューにかけているjs(new.js)は以下の通りです

js

1$(function(){ 2 $('.false-place').keyup(function(){ 3 $('input[name="addr11"]').attr('name', 'event[place]'); 4 }); 5});

試したこと

①バリデーションの順番を変えましたが、効果はありませんでした。
「イベント名(event_name)が存在すること」はバリデーションが効いて「イベント名を入力してください」というエラーメッセージが表示されます。

②mysqlのplaceカラムのヌル許可にチェックを入れデフォルトを「NULL」にしましたが、効果はありませんでした。
画像はテーブルの構造です。
イメージ説明

補足情報(FW/ツールのバージョンなど)

プログラミングを始めて1ヶ月半です。初めての質問でわかりづらい部分もあると思いますが、ご回答いただけると幸いです。
Rails 5.2.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

こんばんは。

該当のエラーは、def geocode .... end のブロックの中で発生しているので、コントローラーからの入力のバリデーションは通っているのでは、と思います。

それよりも、この処理の中で maps.googleapis.com にアクセスしてその結果をJSONに入れていますが、ここの値が想定どおりになっていないのでは?という気がします。
組み立てているURLが正しいか、そのURLでアクセスした結果のJSONが想定どおりのフォーマットになっているかを確認すると良いのでは。

デバッグやputsでプリントさせてみるといかがでしょう。

ruby

1 def geocode 2 # .... 省略 3 puts uri 4 5 res = HTTP.get(uri).to_s 6 response = JSON.parse(res) 7 8 puts response 9 10 self.latitude = response["results"][0]["geometry"]["location"]["lat"] 11 self.longitude = response["results"][0]["geometry"]["location"]["lng"] 12 end

追記

with_options のブロックのところが効いてないのではと思います。
いったん、宣言部分と対応する end の部分をコメントアウトしてチェックするといいのでは。

# with_options presence: true do validates :event_name validates :place # end

追記2

geocodeに入ってきた直後に、errorsの中身をチェックするといいかもしれません。

def geocode puts errors.messages address = (place + place_building) # 本来の’処理 end

投稿2019/11/01 15:16

編集2019/11/02 12:24
suama

総合スコア1997

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mball

2019/11/02 03:13

ご指摘ありがとうございます。 頂いたアドバイスを基に、フォーム上でpriceの値が空の時と空でない時で試しました。ターミナル上で確認したところ、puts uriの時点で ①price="北海道札幌市北区北二十二条西3-2-15"の場合 https://maps.googleapis.com/maps/api/geocode/json?address=%E5%8C%97%E6%B5%B7%E9%81%93%E6%9C%AD%E5%B9%8C%E5%B8%82%E5%8C%97%E5%8C%BA%E5%8C%97%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%9D%A1%E8%A5%BF3-2-15&key=[APIのキー] {"results"=>[{"address_components"=>[{"long_name"=>"Kita 22 Jonishi", "short_name"=>"Kita 22 Jonishi", "types"=>["political", "sublocality", "sublocality_level_2"]}, {"long_name"=>"Kita Ward", "short_name"=>"Kita Ward", "types"=>["political", "sublocality", "sublocality_level_1"]}, {"long_name"=>"Sapporo", "short_name"=>"Sapporo", "types"=>["locality", "political"]}, {"long_name"=>"Hokkaido", "short_name"=>"Hokkaido", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"Japan", "short_name"=>"JP", "types"=>["country", "political"]}, {"long_name"=>"001-0022", "short_name"=>"001-0022", "types"=>["postal_code"]}], "formatted_address"=>"Kita 22 Jonishi, Kita Ward, Sapporo, Hokkaido 001-0022, Japan", "geometry"=>{"bounds"=>{"northeast"=>{"lat"=>43.08919299999999, "lng"=>141.3485081}, "southwest"=>{"lat"=>43.085417, "lng"=>141.3280919}}, "location"=>{"lat"=>43.0874237, "lng"=>141.3404892}, "location_type"=>"APPROXIMATE", "viewport"=>{"northeast"=>{"lat"=>43.08919299999999, "lng"=>141.3485081}, "southwest"=>{"lat"=>43.085417, "lng"=>141.3280919}}}, "place_id"=>"ChIJD8-1AuMoC18RrQYFY4MZqZI", "types"=>["political", "sublocality", "sublocality_level_2"]}], "status"=>"OK"} ②price=""の場合 https://maps.googleapis.com/maps/api/geocode/json?address=&key=[APIのキー] {"error_message"=>"Invalid request. Missing the 'address', 'components', 'latlng' or 'place_id' parameter.", "results"=>[], "status"=>"INVALID_REQUEST"} と出力されました。 考えてみたのですが、 price=""でも after_validation :geocodeが呼び出されているので、 with_options presence: true do validates :event_name validates :place end でplaceに対するバリデーションがうまくいっていないような気がして、、、 お手数おかけしますが、再度ご意見いただければ幸いです、、
mball

2019/11/02 05:12

[訂正] {}のデータはputs responseによって出力されたものでした。
suama

2019/11/02 08:23 編集

こんにちは!失礼しました。 placeがブランクになってしまった上でGoogleのAPIへのリクエストが送信されている。 placeがブランクなら、GoogleのAPIへのリクエスト処理を出す前に、バリデーションエラーになってほしいのにそうならない、ということですね。 with_options のあとの条件式を指定を確認してみてはどうでしょう。 with_optionsの後ろは、if とか unless が来るのではと。 いったん、with_options のブロックに入れない形で、バリデーションをかけて同じリクエストを投げてみては? 単純に、こんなかんじで。 validates :place, presence: true
mball

2019/11/02 08:34

再度アドバイスありがとうございます。 with_optionsのブロックに入れない形でバリデーションをかけました。 validates :event_name, presence: true validates :place, presence: true がしかし、またしても同じエラーが起きてしまいました。。。 with_optionsのブロックに入れても入れなくても event_nameにはバリデーションがかかり「イベント名を入力してください」というエラーメッセージを吐き出すのですが、placeにだけバリデーションがかからず画像を載せている通りエラー画面へ遷移してしまうみたいです。バリデーションがかかっていない理由がどうしてもわかりません。。 何度も申し訳ございませんが、ご意見いただければ幸いです。
suama

2019/11/02 08:53

フォームから’送信したときの、リクエストはどうなっていますか? ログに出ていると思いますので。
suama

2019/11/02 09:44

ありがとうございます! ちなみに、place はテーブルに存在するカラムでしょうか?
suama

2019/11/02 12:22 編集

バリデーションはどちらの場合でもチェックされていると思います。 標準のバリデーションは、errorsに値が入るところまでで、 実際には「追加更新削除」、する直前に、errorsに値があればロールバックするという流れになります。 実際に、event_name、placeどちらをブランクにした場合でも、geocode のメソッドが呼ばれていますよね。 ここでGoogle APIにリクエストを出していますが、この箇所では event_nameは使っていませんよね。 placeが”” の時は、Goggle APIにリクエストを出しても想定した形のJSONが返ってこないので、undefined method `[]' for nil:NilClass が発生し、「saveが走る前」に例外でエラーになってしまいます。 このため、バリデーションエラーがあっても、View側に結果を返せずに50xで画面が赤くなる、と考えられます。 placeがある場合は、Google APIから正しくJSONが取得できて、メソッドが正常に処理されるので save に進みますが、結局ここでevent_nameが無いのでActiveRecordでRollbackが走ります。 このケースなら、コントローラとView側が処理できるので、画面にエラーを返すことができているのだと思います。 def geocode のあとに、 puts errors とか、puts errors.messages と差し込んでみると、多分バリデーションがかかっているかどうか確認できるのでは。 def geocode puts errors.messages address = (place + place_building) ..... end
mball

2019/11/02 12:36

ありがとうございます!! 確かにバリデーションはかかっていたのですが、after_validationでgeocodeメソッドを呼び出していたのでエラー画面に遷移してしまっていたようです!! 親身に答えてくださってありがとうございました!本当に助かりました!!
suama

2019/11/02 12:42

よかったですね! 何往復かしてしまってすみませんが、自分でも書いてて「あ、そうか」とおもってしまいました。
guest

0

原因がわかりました!

after_validation :geocode

この一行があったせいで、place=""(空の時)でもgeocode内のメソッドが呼び出されていました。place=""でも住所の緯度経度を取得しようとしたために出たエラー画面でした。

この一行を消し、event.rb(モデル)内のgeocodeメソッドを定義している前に記述されたprivateを消した後、event_controller.rbへ次の記述をしました。

ruby

1def create 2 @event = Event.new(event_params) 3 if @event.place != "" 4 @event.geocode 5 end 6 if @event.save 7 @event.geocode 8 redirect_to action: 'index' 9 else 10 render "new" 11 end 12end

after_validationの意味や、バリデーションについて理解を深められたエラーでした!
ご協力いただきありがとうございました!!

投稿2019/11/02 12:30

mball

総合スコア7

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suama

2019/11/02 12:45

よかったですね! if @event.place != "" のところは、placeがあるかどうかだけのチェックなので、if event.valid? だと、その他のフィールドもふくめたバリデーションの状態を返してくれます。 よろしければ。
mball

2019/11/02 13:06

なるほど!その記述も後で試します! 初めてteratailで投稿してドキドキしてましたが、またひとつ成長できて楽しいです。 今回は助けてくださってありがとうございました!
guest

0

どれかのオブジェクトがnilになってるのでそのエラーが出てます
response
response["results"]
response["results"][0]
response["results"][0]["geometry"]
response["results"][0]["geometry"]["location"]

このどれかがnilなのでがんばって調べましょう

投稿2019/11/01 15:11

編集2019/11/01 15:12
y_waiwai

総合スコア88038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mball

2019/11/02 05:14

responseの時点で {"error_message"=>"Invalid request. Missing the 'address', 'components', 'latlng' or 'place_id' parameter.", "results"=>[], "status"=>"INVALID_REQUEST"} でした。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問