Shota_U
@Shota_U (Shota Ushiro)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

「Ruby初心者」 投稿したデータの一覧が表示できなくて困っております。

現在家計簿アプリを作成中です。

エラーは出てないのですが、入力した投稿の一覧が見れません。
saveできたときに「登録しました」という文字を出すようにしていますが、その文字は出てきます。

実際の画面です。

1枚目が入力ページ、
2枚目が一覧ページです。


スクリーンショット 2020-07-29 13.40.48.png


スクリーンショット 2020-07-29 13.41.14.png


こんな感じで「登録しました」と出てくるためセーブされているはずが、一覧に表示されません。

関係ありそうな箇所のコードがこちらです。

index.html.erb
<% require 'active_support/core_ext/numeric/conversions' %>
<h2>収入科目の新規データ登録</h2>
<p>登録年月を設定してください</p>
<%= form_tag({controller: :income_values, action: :new}, {method: :post}) do %>
    <input type="month" name="year_month">
    <input type="submit">
<% end %>

<h2>収入科目 データ一覧 </h2>
<% if @income_value.present? %>
<table>
  <tr>
    <th>登録年月</th>
    <th>名称</th>
    <th>値</th>
    <th>備考</th>
    <th>操作</th>
  </tr>
    <% @income_values.each do |income_value| %>
  <tr>
        <td><%= income_value.year_month.strftime('%Y年%m月') %></td>
        <td><%= @incomes.find(income_value.income_id).name %></td>
        <td><%= income_value.value.to_s(:delimited) %> 円</td>
        <td><%= income_value.description %></td>
        <td><%= link_to "編集", [:edit, income_value] %> | <%= link_to "削除", income_value, method: :delete, data: { confirm: "本当に削除しますか?"} %></td>
  </tr>
  <% end %>
</table>
<% else %>
    <p>登録されているデータがありません。</p>
<% end %>

income_values_controller.rb
class IncomeValuesController < ApplicationController

    def index
      @incomes = Income.order(created_at: :asc)
        @income_values = IncomeValue.order("year_month asc")
    end

    def show
        @income_value = IncomeValue.find(params[:id])
    end

    def new
        year_month_day = params[:year_month] + "-01"
        @year_month = year_month_day.to_date

        @incomes = Income.order(created_at: :asc)
        @form = Form::IncomeForm.new
    end

    def edit
        @income_value = IncomeValue.find(params[:id])
        @income = Income.find(@income_value.income_id)
    end

    def create
        @form = Form::IncomeForm.new(income_form_params)
        if @form.save
            redirect_to :income_values, notice: "登録しました"
        else
            redirect_to :income_values, notice: "登録に失敗しました"
        end
    end

    def income_form_params
        params
            .require(:form_income_form)
            .permit(income_values_attributes: Form::IncomeValue::REGISTRABLE_ATTRIBUTES)
    end

    def update
        @income_value = IncomeValue.find(params[:id])
        @income_value.assign_attributes(params[:income_value])
        if @income_value.save
            redirect_to :income_values, notice: "情報を更新しました"
        else

インターンへ向けてのポートフォリオ作成中なのですが
ここで詰まってしまい、2日ほど試行錯誤してお手上げ状態です、、、

プログラミング初心者で質問も初めてなので至らない点が多いと思います、すみません

もしよろしければ、ご教授の程よろしくお願いします。


※補足です

ご回答いただいたように
<% if @income_value.present? %>

<% if @income_values.present? %>
としましたところ、

このようなエラーが出てしまいました。

スクリーンショット 2020-07-30 0.42.35.png

SQLの方に問題があるということでしょうか?

もし、エラーの原因がお分かりでしたら
ご教授願いたいです🤲


さらに補足です

スクリーンショット 2020-07-30 14.19.26.png

ご回答いただいた通りにやったところ、このようなエラーが出ました。
これはなんだかよく見るエラーです。

NillClassにstrftimeなんていうメソッドはない
ということで、
調べながら色々と手を動かしてみましたが、このエラーからもなかなか抜け出せません。

もし、エラーの原因がお分かりでしたら
ご教授願いたいです🤲

0

6Answer

index アクションで @income_values に値をセットしていますが,ビューでは

if @income_value.present?

となっていて,変数名が違う(複数形と単数形)のが原因かな。

1Like

Comments

  1. @Shota_U

    Questioner

    ご回答ありがとうございます😊

    if @income_values.present?

    とすると、ActiveRecord::StatementInvalid in IncomeValues#index
    と出てきてしまいます、、、
  2. @Shota_U

    Questioner

    質問に追加でエラーのスクショ載せてありますので、見ていただければありがたいです

@scivola さん

nil, “”, “ “(半角スペースのみ), 空の配列, 空のハッシュのときにfalseを返します

なので、present?でも大丈夫かなという感じがします。

1Like

Comments

  1. @Shota_U

    Questioner

    @scivola さん、ご回答ありがとうございます😊

    <% if @income_values.present? %>
    とすると、
    ActiveRecord::StatementInvalid in IncomeValues#index

    というエラーが出てきてしまいます、、、
  2. @Shota_U

    Questioner

    質問に追加でエラーのスクショが載せてありますので、見ていただければありがたいです

@studio15 さん
そうですね,私の勘違いでした。
レコードが 1 件以上あるかどうかを確認するのは,元のコードのとおり

@income_values.present?

で OK ですね。勉強になりました。
@Shota_U さん,混乱させてしまってすみません。

以下,調べて分かったことを少し説明します。
私が早とちりした理由は,

IncomeValue.order("year_month asc")

の返り値は配列とかではなく IncomeValue::ActiveRecord_RelationActiveRecord::Relation のサブクラス)のインスタンスであることでした。

しかし,ActiveRecord::Relation には empty? メソッドが実装されていました。これで,レコードが 0 件かどうかを判定します。present? は,この empty? に基づいて動作します。だから present? でいいんですね。

なお,

nil, “”, “ “(半角スペースのみ), 空の配列, 空のハッシュのときにfalseを返します

は不正確です(このサイトは誤りを指摘しようにも連絡先が分からないんですよね)。
String 以外は empty? があればそれに基づきますし,String では半角スペースのほか,改行・タブ・全角スペースなどなどが入っていても blank? は真になります。

1Like
<% if @income_value.present? %>

<% if @income_values.present? %>

に変更でどうでしょう。

「登録しました」と出てくるためセーブされているはずが

データが保存されているかどうかはビューにどう表示されているかより、Railsサーバーのログや、SQLのテーブルのデータ自体を参照したほうが確実なので、そちらも見てみると良いかもしれません。

0Like

Comments

  1. もたもたと書いていたらかぶってしまった…😂

【追記:このコメントは誤りでした。この下のコメント参照】

なお,変数名を揃えた場合,

@income_values.present?

は(レコードが 1 件も無くても)常に真となって,意図と違う判定になるのではないかと思います。

IncomeValue モデルのレコードが 1 件以上存在することを確認するなら

@income_values.count > 0

かな。

0Like

Qiita の質問て,回答が時系列順位並ばないので「下のコメント」とか書くとワケわからなくなるんですね。

さて,ActiveRecord::StatementInvalid in IncomeValues#index の件ですが,order の指定の仕方が怪しい気がします。
ここから先は rails console でやっていきましょう。そのほうが早いと思います。
rails console 上で,

IncomeValue.order("year_month asc")

と打ってみてください。

先に進む前にちょっと説明ですが,上記のコードだけでは,実際には検索は行われません。先に書いたようにこのコードは IncomeValue::ActiveRecord_Relation のインスタンスを返すだけだからです。
ところが,rails console 上で上記のコードを実行させるとIncomeValue::ActiveRecord_Relation を文字列化しようとして(表示するためには文字列化する必要がありますね!),その際に検索が実行される仕様になっています。

おそらく ActiveRecord::StatementInvalid が出るでしょう。
繰り返しますが,エラーが出るのは IncomeValue.order("year_month asc") を評価した時点ではなく,それを文字列化しようとして検索が行われるタイミングです。

こういうとき,どんな SQL で検索を行おうとしたのか確認します。それには,rails console 上で

IncomeValue.order("year_month asc").to_sql

とします。
こうすると,IncomeValue::ActiveRecord_Relation オブジェクトから生成される SQL 文が端末に表示されます。

さて,推測ですが,order の引数が "year_month asc" なのが怪しいと思いました。
asc(昇順)はデフォルトなので省略してもよく,

IncomeValue.order("year_month")

とか

IncomeValue.order(:year_month)

とか書けば目的がかなうはずです。
もしこれでエラーが出なくなるなら,やはり引数の与え方ですね。
これらを to_sql して何が違うのかを調べてみましょう。

さて,降順に並べたい場合や,昇順だけどどうしてもあらわに asc を指定したい場合はどう書くか。
ふつうは

IncomeValue.order(year_month: "asc")

のようにハッシュ形式で引数を与えると思います。
これも to_sql してみて,どう違うかを確認してみましょう。

結果を教えてくださいね。

0Like

Comments

  1. @Shota_U

    Questioner

    @scivolaさん、ありがとうございます🙇🏻‍♂️

    rails console上で、
    IncomeValue.order("year_month asc")
    としましたところ、おっしゃった通りのエラー文が出ました。

    次にIncomeValue.order("year_month asc").to_sql
    としたところ
    => "SELECT `income_values`.* FROM `income_values` ORDER BY year_month asc"

    という文章が出てきました。



    IncomeValue.order("year_month")

    としましたら、先ほどと変わらないエラーが出ました。

    次に


    IncomeValue.order(:year_month)


    としたところ今度は、NoMethodError in IncomeValues#index
    というエラーが出てきてしまいました、、、

    ※また質問の方に、追加でエラーの画像を載せておきます

    よろしくお願いいたします
  2. @Shota_U

    Questioner

    このエラーから、色々調べてやってみた結果、正常に表示することができました!!
    本当にありがとうございます!
    非常に助かりました😁
  3. 先を急ぐ前にちょっと整理しましょう。
    `order` メソッドに文字列を与えた場合,SQL 文ではそれがそのまま `ORDER BY` のあとに入る感じですね?
    で,`ORDER BY year_month` とか `ORDER BY year_month asc` とかだとエラーになる,と。
    `order(:year_month)` とか `order(year_month: "asc")` とかだとエラーにならないわけですね?
    (合ってますか?)
    そうだとすると,`order` の使い方に関しては解決したということになりますね。

    そして今抱えている問題はその次の段階ということですね?
    index アクションのビューで NoMethodError が出る,と。

    `undefined method `strftime' for nil:NilClass` の「NilClass に strftime なんてメソッドはねえよ」という解釈は合っています。
    そして,エラー画面からは

    ```
    income_value.year_month.strftime('%Y年%m月')
    ```

    の箇所でエラーが出ていると分かります。
    であれば,`income_value` に対する `year_month` メソッドの返り値が `nil` だった,ということですね。

    これは,実際 `year_month` カラムの値が `nil` なんじゃないでしょうか(データベースのほうでいうと `NULL` になっている)。

    ここから先の対応は二手に分かれると思います。
    `year_month` の値が `nil` ということが「あってはならない事態」なのであれば,そのようなレコードがデータベースに出来てしまったことが問題なので,そうならない策を講じ,現在のレコードは削除します。
    一方,`nil` が「ありうる事態」なのであれば,`nil` でもエラーにならないビューの書き方に変えることになりますね。
  4. あ,書いてるうちに解決しちゃったのか。メデタシメデタシ。
  5. @Shota_U

    Questioner

    こんなに丁寧に回答していただき、非常にありがたいです!

    書いてる途中でしたか、、、すみません。。。



    ちなみに!
    NoMethodErrorを解決した方法としましては、

    ```
    income_value.year_month.strftime('%Y年%m月')
    ```



    ```
    income_value.year_month&.strftime('%Y年%m月')
    ```
    としました

    すると、また別のエラーで、ArgumentError
    が出てきました。

    エラー文言をよく見て調べてみたところ、


    ```index.html.erb
    <tr>
    <td><%= income_value.year_month.strftime('%Y年%m月') %></td>
    <td><%= @incomes.find(income_value.income_id).name %></td>
    <td><%= income_value.value.to_s(:delimited) %> 円</td>
    <td><%= income_value.description %></td>
    <td><%= link_to "編集", [:edit, income_value] %> | <%= link_to "削除", income_value, method: :delete, data: { confirm: "本当に削除しますか?"} %></td>
    </tr>

    ```

    の、上から3行目

    ```
    <td><%= income_value.value.to_s(:delimited) %> 円</td>

    ```

    の部分の引数が0のはずなのに、1個入ってしまってるよ

    という意味のエラーだということが分かりました

    そこで、引数を消したところ、正常に作動しました。


    @scivolaさんの丁寧なアドバイスのおかげで
    僕は本当に助かりました、ありがとうございます!


    また機会があれば、その他の知識も教えていただけると非常にうれしいです!

    今回は本当にありがとうございました!
  6. `to_s(:delimited)` って,`1234` を `"1,234"` みたいに 3 桁区切りのカンマを入れた文字列にする意図だったんですよね?

    この `:delimited` とかのオプションが使える `to_s` は Rails(より詳しく言えば active_support)によって再定義されたメソッドですが,レシーバーが数値の場合に使うものですね。
    「引数は 0 個のはずやんけ!」と文句を言われたのだとすると,`income_value.value` の値が数値ではなかった(たとえば文字列だった)のではありませんか?
  7. @Shota_U

    Questioner

    `to_s(:delimited)`は、そういう意味だったんですね

    ネット上の家計簿アプリのコードを参考にして書いていたので、そこの意味は把握しておりませんでした。

    「income_value.value の値が数値ではなかった」といいますと、
    投稿した内容が文字列だったのではないか
    ということでしょうか?
  8. いえ,ブラウザーのフォームから送られるリクエストでは,パラメーターはすべて文字列です。
    たとえ数値を入力させても,それは**数字列**という文字列になります。
    それを Rails アプリが受け取ってデータベースに格納するわけですが,文字列のままか整数や浮動少数点数などの数値に変換するかはデータベースのテーブル定義によります。

    IncomeValue モデルを作ったときにマイグレーションを書きましたよね。そのとき `value` の型を何にしたか,ということです。
    `integer` とかにしたのなら `income_value.value` の返り値は Integer か `nil` かどちらかのはずなので・・・あっ! 分かった。
    エラーからして `income_value.value` の返り値がきっと文字列か何かだったんだろうと推測したのですが,そうじゃなくて `nil` だったんですね。

    えっと,まず `value` の型が何なのかを確認してください。
    `integer` などの数値型だったとすると,エラーの原因は `nil` が返ったことだと思います。
    そうすると,対処方法は例の `&.`(safe navigation operator;俗称「ぼっち演算子」)ですね。
    つまり,

    ```rb
    income_value.value&.to_s(:delimited)
    ```

    としておけば,数値のときは狙い通り 3 桁区切りのカンマが入りますし,`nil` のときはこの式全体の値が `nil`(なので,ビューに埋め込むと空文字列になる)となります。
  9. @Shota_U

    Questioner

    なるほど

    そうです!ValueはInteger でした!

    そして、income_value.value&.to_s(:delimited)
    としたところ、思った通りにコンマ付きで表示できました!

    本当にありがとうございます(^-^)

Your answer might help someone💌