konchanxxx / menta

MENTAのタスク管理用リポジトリ
0 stars 0 forks source link

rails|nestしたモデルのformで編集ができない。 #54

Closed yoshimitsu41 closed 5 years ago

yoshimitsu41 commented 5 years ago

概要

client(クライアント) agreement(契約) というモデルがあります。

client 1 - * agreement の関係です。

#routes.rb
  resources :clients do
    resources :comments
    resources :agreements
  end

困っていること

クライアントに紐付いた契約を追加できるように実装して、追加や削除はできています。 しかし、編集ボタンを押すとエラーがでて解決方法がわかりません。

views/agreements/edit.html.erb
<%- model_class = Agreement -%>
<div class="page-header">
  <h1><%=t '.title', :default => [:'helpers.titles.edit', 'Edit %{model}'], :model => model_class.model_name.human.titleize %></h1>
</div>
<%= render :partial => 'form' %>
views/agreements/_form.html.erb
<%= form_for @comment, url:clients_path, :html => { :class => "form-horizontal comment" } do |f| %>

  <% if @comment.errors.any? %>
    <div id="error_expl" class="panel panel-danger">
      <div class="panel-heading">
        <h3 class="panel-title"><%= pluralize(@comment.errors.count, "error") %> prohibited this comment from being saved:</h3>
      </div>
      <div class="panel-body">
        <ul>
        <% @comment.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
        </ul>
      </div>
    </div>
  <% end %>

  <div class="form-group">
    <%= f.label :body, :class => 'control-label col-lg-2' %>
    <div class="col-lg-10">
      <%= f.text_field :body, :class => 'form-control' %>
    </div>
    <%=f.error_span(:body) %>
  </div>
  <div class="form-group">
    <%= f.label :user_id, :class => 'control-label col-lg-2' %>
    <div class="col-lg-10">
      <%= f.text_field :user_id, :class => 'form-control' %>
    </div>
    <%=f.error_span(:user_id) %>
  </div>
  <div class="form-group">
    <%= f.label :client_id, :class => 'control-label col-lg-2' %>
    <div class="col-lg-10">
      <%= f.text_field :client_id, :class => 'form-control' %>
    </div>
    <%=f.error_span(:client_id) %>
  </div>

  <div class="form-group">
    <div class="col-lg-offset-2 col-lg-10">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
                clients_path, :class => 'btn btn-default' %>
    </div>
  </div>

<% end %>
http://localhost:3000/clients/1/agreements/8/edit
のような画面のformのアクションが下記のようになっています。
<form class="form-horizontal agreement" id="edit_agreement_8" action="/clients" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓"><input type="hidden" name="_method" value="patch">
<input type="hidden" name="authenticity_token" value="vD0qp35lJ+9QyWgsLIwGxIVkd4f+mNSpKrOwhi1jC3vaoxEMgK2519nboPpjNzv5Rl1jNRfRp3shOGE1dEP14w==">
#省略

action="/clients" の部分が action="clients/1/agreements/8" になっていないと情報が渡らないと考えているのですがどのようにすればいいのかわかりません。

ご教授お願いします。

konchanxxx commented 5 years ago

編集ボタンを押すとエラーがでて解決方法がわかりません。

まずエラー文言等をそのまま貼り付けて頂きたいです:bow: 後ソースコードはGitHubリンクを貼り付けてもらった方が良いかもです。コピペミスとかもあるので:bow:

$ bundle exec rails routes | grep client

した結果ってどうなりますか?

yoshimitsu41 commented 5 years ago

後ソースコードはGitHubリンクを貼り付けてもらった方が良いかもです。コピペミスとかもあるので🙇

https://github.com/yoshimitsu41/greenhill.git

$ bundle exec rails routes | grep client した結果ってどうなりますか?

                    root GET    /                                                 clients#index
         client_comments GET    /clients/:client_id/comments(.:format)            comments#index
                         POST   /clients/:client_id/comments(.:format)            comments#create
      new_client_comment GET    /clients/:client_id/comments/new(.:format)        comments#new
     edit_client_comment GET    /clients/:client_id/comments/:id/edit(.:format)   comments#edit
          client_comment GET    /clients/:client_id/comments/:id(.:format)        comments#show
                         PATCH  /clients/:client_id/comments/:id(.:format)        comments#update
                         PUT    /clients/:client_id/comments/:id(.:format)        comments#update
                         DELETE /clients/:client_id/comments/:id(.:format)        comments#destroy
       client_agreements GET    /clients/:client_id/agreements(.:format)          agreements#index
                         POST   /clients/:client_id/agreements(.:format)          agreements#create
    new_client_agreement GET    /clients/:client_id/agreements/new(.:format)      agreements#new
   edit_client_agreement GET    /clients/:client_id/agreements/:id/edit(.:format) agreements#edit
        client_agreement GET    /clients/:client_id/agreements/:id(.:format)      agreements#show
                         PATCH  /clients/:client_id/agreements/:id(.:format)      agreements#update
                         PUT    /clients/:client_id/agreements/:id(.:format)      agreements#update
                         DELETE /clients/:client_id/agreements/:id(.:format)      agreements#destroy
                 clients GET    /clients(.:format)                                clients#index
                         POST   /clients(.:format)                                clients#create
              new_client GET    /clients/new(.:format)                            clients#new
             edit_client GET    /clients/:id/edit(.:format)                       clients#edit
                  client GET    /clients/:id(.:format)                            clients#show
                         PATCH  /clients/:id(.:format)                            clients#update
                         PUT    /clients/:id(.:format)                            clients#update
                         DELETE /clients/:id(.:format)                            clients#destroy

になります!

konchanxxx commented 5 years ago

ありがとうございます:bow:

url:clients_path をフォームで指定していますがその定義で実行されるアクションってroutesで出力したアクションのどれになるかわかりますか?

yoshimitsu41 commented 5 years ago

url:clients_path をフォームで指定していますがその定義で実行されるアクションってroutesで出力したアクションのどれになるかわかりますか?

すみません。 わからないです。

views/agreements/_form.html.erb
<%= form_for @comment, url:clients_path, :html => { :class => "form-horizontal comment" } do |f| %>

のurl:clients_pathのことですよね?

edit画面は下記です。 image

update agreementのボタンをクリックするとルーティングエラーになります。

image

url:edit_client_agreement_path に変更してもルーティングエラーになりました。

image

konchanxxx commented 5 years ago

ですです! お、わからないのは全然おkですがここは必須で理解しておきたいところなのでこれを機に理解しましょう!

No route matches [PATCH] "/clients" のところは PATCHメソッドの /clients がリクエストされているということです。これがform_forでリクエスト先として生成されています。

clientsで生成されているルーティングを確認すると下記になるので GETPOSTしかなくてPATCH になっているものがないですよね。それでルーティング定義がないのでエラーになっています。

                 clients GET    /clients(.:format)                                clients#index
                         POST   /clients(.:format)                                clients#create

じゃあ今回生成したいリクエストに使用するURL(endpointとか言ったりします)を生成するためにはどうしたら良いのかという話になるのですが、まずルーティング定義からルーティングしたい先のcontrollerとアクションを探します。 今回だと agreements_controllerのupdateアクションを実行したいと思うので agreements#updateとなっているところを探します。

        client_agreement GET    /clients/:client_id/agreements/:id(.:format)      agreements#show
                         PATCH  /clients/:client_id/agreements/:id(.:format)      agreements#update
                         PUT    /clients/:client_id/agreements/:id(.:format)      agreements#update

二つあるんですが、これは昔からの名残でPUTの方が残っているので基本的にはPATCHを利用すれば良いです。なので client_agreement_path というurl(endpoint)とmethodはpatchを指定すれば良いです。

Rails5系からはform_forが非推奨になっているようなのでform_withを使ってあげると良いと思います。 https://techracho.bpsinc.jp/hachi8833/2017_05_01/39502

form_with @agreement, url: client_agreement_path, scope: :patch do |f|

みたいな書き方になるのかな。 ただ url: client_agreement_path, scope: :patch のところは本来Railsが自動で判別してくれるので不要かもしれません。

あと気になったのは記載していただいたform_forで@commentインスタンスを利用しようとしている点?ですかね。@agreementの情報を編集するのにcommentは関係ないのでは?と思ったりしました。

ついでに

url:edit_client_agreement_path に変更してもルーティングエラーになりました。

のところはeditアクションを実行するパスにルーティングしようとしていると思うのですが、editアクションって編集画面を表示するためのアクションなのでそこにルーティングしても意味ないと思いました:bow:

ちょっと難しいかもですが一旦ご確認くださいmm 難しいところあれば追加で質問ください:bow:

yoshimitsu41 commented 5 years ago

なんとなくですが、理解しました。

form_with @agreement, url: client_agreement_path, scope: :patch do |f|

で試したところエラーが出ました。 ※url: client_agreement_path, scope: :patchの無しのバージョンも試しましたがエラーでした。 image

しかし

<%= form_for @agreement, url:client_agreement_path, :html => { :class => "form-horizontal agreement" } do |f| %>

で試したところエラーなく更新ができました。 ありがとうございます!

konchanxxx commented 5 years ago

ああ、form_withのパラメータの指定の仕方が間違ってましたね

form_with model: @agreement, url: client_agreement_path, scope: :patch do |f|

とかが正しいのか。

scopeはなくてもRailsが更新する処理かどうか判定してルーティングしてくれます。 判定する基準は @agreementのidが設定されているかどうかで

設定されていない => 新規作成と判定してcreateアクションを実行 設定されている => 更新と判定してupdateアクションを実行

という挙動になります。 form_forは非推奨の記述になって今後なくなっていくのでなるべく使わない方が良いですね:smile:

yoshimitsu41 commented 5 years ago

ありがとうございます!

konchanxxx commented 5 years ago

@yoshimitsu41 これとか参考になるので復習してみると良いと思います:bow: https://diveintocode.jp/tips/routing01