@seiyarick (seiya)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

ActiveRecord::RecordNotFoundエラーについて

解決したいこと

管理者側、ユーザー側の存在する注文機能をもつWebアプリケーションを作っています。
注文情報確認画面のレイアウトを調整し確認するためにリロードをする際にエラーが発生したので解決のヒントとなる様なことを教えて頂きたいです。

注文情報確認画面(/orders/confirm.html.erb)
注文履歴詳細画面(/orders/show.html.erb)

発生している問題・エラー

ActiveRecord::RecordNotFound in Public::OrdersController#show
Couldn't find Order with 'id'=confirm

↑「id'=confirm'のオーダーが見つかりません。」となります。

def show
    @order = Order.find(params[:id])
    @ordering_details= @order.ordering_details
    @order.shipping_cost = 800
    @total_price = 0

↑の@order = Order.find(params[:id])がエラー箇所です。

*/public/orders_controller.rb

def confirm
    @order = Order.new
    @cart_items = current_customer.cart_items
    @order.shipping_cost = 800
    @total_price = 0
    @cart_items.each do |cart_item|
     @total_price += cart_item.item.add_tax_price*cart_item.amount
    end
    @order.total_payment = @total_price + @order.shipping_cost

  #@shipping_cost = params[:order][:shipping_cost]
    @order.payment_method = params[:order][:payment_method]
    #ご自身住所選択時
      if params[:order][:select_number] == 0.to_s#paramsはカラムを持ってくるため、[]は持ってきたデータを受け取る記述
       @order.postal_code = current_customer.postal_code
       @order.address = current_customer.address
       @order.name = current_customer.first_name + current_customer.last_name

    #登録済み住所選択時
      elsif params[:order][:select_number] == 1.to_s
        address = current_customer.addresses.find(params[:order][:address_id].to_i)#[:order][:address_id]の文字列を数字に変えるto_iを記述

        @order.postal_code = address.postal_code#上段で持ってきたaddressのidのpostal_codeを右辺に送る。
        @order.address = address.address
        @order.name = address.name

    #新しいお届け先選択時
      elsif params[:order][:select_number] == 2.to_s

        @order.postal_code = params[:order][:postal_code]#フォームで入力したデータをparamsが持ってきて[:order][:postal_code]で郵便番号を呼び出す。
        @order.address = params[:order][:address]
        @order.name = params[:order][:name]

      end

  end
def show
    @order = Order.find(params[:id])
    @ordering_details= @order.ordering_details
    @order.shipping_cost = 800
    @total_price = 0
    @ordering_details.each do |ordering_detail|
     @total_price += ordering_detail.item.add_tax_price*ordering_detail.amount
    end
    @order.total_payment = @total_price + @order.shipping_cost

  end
private
  def order_params
    params.require(:order).permit(:postal_code, :address, :name, :payment_method, :customer_id, :total_payment)
  end
end

*/public/orders/show.html.erb

html.erb
  <td><%= @order.created_at.strftime('%Y/%m/%d') %></td>
    </tr>
    <tr>
      <th>配送先</th>
      <td>
        <%= @order.postal_code %>
        <%= @order.address %>
        <%= @order.name %>
      </td>
    </tr>
    <tr>
      <th>支払方法</th>
      <td><%= @order.payment_method %></td>
    </tr>
    <tr>
      <th>ステータス</th>
      <td></td>
    </tr>
  </tbody>
</div>

<h6>請求情報</h6>
<tbody>
  <tr>
    <th>商品合計</th>
    <td><%= (@order.total_payment - @order.shipping_cost).to_s(:delimited, delimiter: ',') %></td>
  </tr>
  <tr>
    <th>配送料</th>
    <td><%= @order.shipping_cost %></td>
  </tr>
  <tr>
    <th>ご請求額</th>
    <td><%= (@order.total_payment + @order.shipping_cost).to_s(:delimited, delimiter: ',') %></td>
  </tr>
</tbody>

<h6>注文内容</h6>
<thead>
  <tr>
    <th>商品</th>
    <th>単価(税込)</th>
    <th>個数</th>
    <th>小計</th>
  </tr>
</thead>
<tbody>
  <tr>
    <% @ordering_details.each do |ordering_detail| %>
    <td><%= ordering_detail.item.name %></td>
    <td><%= ordering_detail.price.to_s(:delimited, delimiter: ',')  %></td>
    <td><%= ordering_detail.amount %></td>
    <td><%= (ordering_detail.price * ordering_detail.amount).to_s(:delimited, delimiter: ',') %></td>
    <% end %>
  </tr>

*/public/orders/confirm.html.erb

html.erb
    <tr>
            <th>商品名</th>
            <th>単価(税込)</th>
            <th>数量</th>
            <th>小計</th>
          </tr>
        </thead>
        <tbody>
          <% @cart_items.each do |cart_item| %>
          <tr><%= image_tag cart_item.item.image, size: "70x50" %><%= cart_item.item.name %></tr>
          <tr><%= cart_item.item.add_tax_price.to_s(:delimited, delimiter: ',') %></tr>
          <tr><%= cart_item.amount %></tr>
          <tr><%= ((cart_item.item.add_tax_price)*(cart_item.amount)).to_s(:delimited, delimiter: ',') %></tr>
          <% end %>
        </tbody>
      </div>
    </div>
    <div class="col-4">
      <table class="table">
        <tbody>
          <tr>
            <th>送料</th>
            <td><%= @order.shipping_cost.to_i %></td>
          </tr>
          <tr>
            <th>商品合計</th>
            <td><%= @total_price.to_s(:delimited, delimiter: ',')  %></td>
          </tr>
          <tr>
            <th>請求金額</th>
            <td><%=  @order.total_payment.to_s(:delimited, delimiter: ',') %></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<h5>支払方法</h5>
<%= @order.payment_method_i18n %>
<h5>お届け先</h5>
〒<%= @order.postal_code %>
<%= @order.address %>
<%=  @order.name %>

<%= form_with model: @order, url: public_orders_path do |f| %>
<%= f.hidden_field :payment_method, :value => @order.payment_method %>
<%= f.hidden_field :postal_code, :value => @order.postal_code %>
<%= f.hidden_field :address, :value => @order.address %>
<%= f.hidden_field :name, :value => @order.name %>
<%= f.hidden_field :customer_id, :value => @order.customer_id %>
<%= f.hidden_field :total_payment, :value => @order.total_payment %>
<%= f.hidden_field :shipping_cost, :value => @order.shipping_cost %>
<%= f.submit "注文を確定する", class:"btn btn-success" %>
<% end %>

*/db/schema.rb(追記)

``` create_table "addresses", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "name"
    t.string "postal_code"
    t.string "address"
    t.integer "customer_id"
  end

  create_table "admins", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["email"], name: "index_admins_on_email", unique: true
    t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
  end

  create_table "cart_items", force: :cascade do |t|
    t.integer "amount"
    t.integer "item_id"
    t.integer "customer_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "customers", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "last_name"
    t.string "first_name"
    t.string "last_name_kana"
    t.string "first_name_kana"
    t.string "postal_code"
    t.string "address"
    t.string "telephone_number"
    t.boolean "is_deleted", default: false
    t.index ["email"], name: "index_customers_on_email", unique: true
    t.index ["reset_password_token"], name: "index_customers_on_reset_password_token", unique: true
  end

  create_table "genres", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "items", force: :cascade do |t|
    t.string "name"
    t.text "introduction"
    t.integer "price"
    t.boolean "is_active"
    t.integer "genre_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "ordering_details", force: :cascade do |t|
    t.integer "price"
    t.integer "amount"
    t.integer "order_id"
    t.integer "item_id"
    t.integer "making_status"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "orders", force: :cascade do |t|
    t.integer "customer_id"
    t.string "postal_code"
    t.string "address"
    t.string "name"
    t.integer "shipping_cost"
    t.integer "total_payment"
    t.integer "payment_method"
    t.integer "order_status"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
end

### 自分で試したこと
「注文 ー<  注文詳細  >ー  商品」はアソシエーションの関係になっており、注文履歴詳細ページには注文(Orderモデル)の持つ「配送先、支払い方法、ステータス」、商品(Itemモデル)の持つ「商品名、単価」の情報がそれぞれ必要になってくる思います。このことから @order=Order.find(params[:id]) の記述ではOrderモデルのカラムしか持って来れていないのでItemモデルのカラムを持ってくる記述が必要と考えていますがどのように書けば良いかわかりません。
ヒントとなる記述を教えていただけると幸いです。
よろしくお願いします。


0 likes

1Answer

config/routes.rb に

get 'orders/:id', to: 'orders#show'

のように書いてあると思いますが、ここで確認画面の URL /orders/confirm にアクセスしたとき、 confirm の部分が :id にマッチするため、 params[:id] に文字列 'confirm' が入ってしまいます。

そして orders_controller.rb の show メソッドが実行され、

def show
    @order = Order.find(params[:id])

の部分で id が 'confirm'Order を取得しようとしてエラーが出ています。(Order の id は連番の数字であり文字列にはなり得ません。)

問題を解決するには URL /orders/confirm にアクセスしたら confirm メソッドが実行されるように routes を設定する必要があります。具体的には以下の順番で書いてください。

get 'orders/confirm', to: 'orders#confirm'
get 'orders/:id', to: 'orders#show'

このように、固定文字列の route は一部がパラメータの route より先に書くのが原則です。

0Like

Comments

  1. @seiyarick

    Questioner

    丁寧に解答いただきありがとうございます。

    今現在のroutesはこのようになっています。

    ```ruby
    namespace :public do
    get 'homes/top'
    get 'homes/about'
    root 'homes#top'
    get 'orders/thanks' => 'orders#thanks'
    post 'orders/confirm' => "orders#confirm"
    get 'order/:id' => 'orders#show'
    patch '/customers/update_delete' => 'customers#update_delete'
    get 'customers/confirm' => "customers#confirm"
    delete 'cart_items' => 'cart_items#all_destroy', as: 'all_destroy'
    resources :addresses, only: [:index, :edit, :create, :update, :destroy]
    resources :orders, only: [:new, :index, :create]
    resources :cart_items, only: [:index, :create, :update, :destroy]
    resources :customers, only: [:show, :edit, :update]
    resources :items, only: [:index, :show, :create]
    end
    ```
    解答いただいた内容に書き換えて注文情報入力画面から注文情報確認画面に遷移しようとした時に以下のエラーが出てしまいます。
    ```
    Routing Error
    No route matches [POST] "/public/orders/confirm"
    ```

    以下の記述だと注文確認画面に遷移できるのですが遷移したページでリロードボタンを押すと更に下のルーティングエラーとなってしまいます。

    *routes

    ``ruby
    post 'orders/confirm', to: 'orders#confirm'
    get 'orders/:id', to: 'orders#show
    ```
    *エラー

    ```
    Routing Error
    No route matches [GET] "/public/orders/confirm"
    ```
    別の原因が考えられるのでしょうか?

  2. 元々の記述が

    post 'orders/confirm' => "orders#confirm"
    get 'order/:id' => 'orders#show'

    だとすると、そもそも確認画面でリロードして

    ActiveRecord::RecordNotFound in Public::OrdersController#show
    Couldn't find Order with 'id'=confirm

    のエラーが出るはずはなく、

    Routing Error
    No route matches [GET] "/public/orders/confirm"

    になりそうなものですが……

    それは置いておくとして、注文情報入力画面から注文情報確認画面に遷移するときは HTTP の POST メソッドが使われるので

    post 'orders/confirm', to: 'orders#confirm'

    の route にマッチします。注文情報確認画面をリロードするときは GET メソッドが使われるので

    get 'orders/confirm', to: 'orders#confirm'

    の route にマッチします。どちらにも対応するなら両方の route を書く必要があります。(ちなみに to: でも => でも意味は同じなのでどちらの記法でも構いません。)
  3. ルーティングエラーを直したとしても、今の注文情報確認画面はリロードにまったく対応していません。リロードするとパラメータが引き継がれないため confirm メソッド内で params[:order] などが空になります。
  4. @seiyarick

    Questioner

    昨日生じていた問題が解決しました。

    原因は<div></div>を使い<%= form_with %><% end %>を囲う際に完全に囲えておらず</div>の後に<% end %>となっている為でした。

    ページは今載せてあるページ以外の注文情報入力画面になります。

    色々お教えいただきありがとうございました。
    以後、同じような挙動になった場合注意してみていきます。

Your answer might help someone💌