Rails 4: form_for with associations

2019-06-24 06:33发布

New to rails and ruby. Struggling with form_for and associations. I am trying to setup a rails app that allows a user to select from a list of clients. The clients are associated via has_many :through relationships. I have the models working as expected and I can add clients to users via the rails console. I now want to move this capability to the web interface. The below code is how I have tried, but it is just not making sense to me. I am unsure which controller action is the correct action. Should I process the form POST in the create action of the client? I am not actually wanting to create a new client, I just want the user to select from a list of existing clients and create the association.

My Models are as follows:

class Client < ActiveRecord::Base
has_many :users, :through => :user_clients
has_many :user_clients, :dependent => :destroy
end

class User < ActiveRecord::Base
has_many :clients, :through => :user_clients
has_many :user_clients, :dependent => :destroy
end

class UserClient < ActiveRecord::Base
belongs_to :user
belongs_to :client
end

Routes as follows

resources :clients do
  resources :users
end

resources :users do
  resources :clients
end 

Clients Controllers

class ClientsController < ApplicationController
  before_action :set_client, only: [:show, :edit, :update, :destroy]

  def index
    if params[:user_id]
      @clients = User.find_by_id(params[:user_id]).clients
    else
      @clients = Client.all
    end
    @clients
  end

  def create
    if params[:user_id]
      user = User.find(params[:user_id])
      client = Client.find_by_id(params[:client_id])
      user.clients << client
      user.save
      redirect_to users_url
    else
      @client = Client.new(client_params)
      @client.save
      redirect_to clients_url
    end
  end
end

Form in the client view

<h1>new.html.erb</h1>
<% if params[:user_id] %>
  <%= form_for([@user,@client]) do |f| %>
  <div class="field">
    <%= f.label :id %><br>
    <%= f.text_field :id %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
  <% end %>
<% else %>
  <%= render 'form' %>
<% end %>

1条回答
淡お忘
2楼-- · 2019-06-24 06:52

First, take a look at that gem: Nested Form. Here's also a couple of railscasts on the topic. However, if you're willing to build your own solution, you can start by reading documentation on nested attributes here.

Hope it helps!

Update. About your routes and everything.

resources :clients do
  resources :users
end

resources :users do
  resources :clients
end 

You've nested resources here. Like you mentioned in your comment, users/:user_id/clients/new and clients/new will lead to the same action. That's the way this routing works. You're correct about Rails style of doing things - that's pretty bad to make some scary logic checks in controller and do, basically, different things inside of one action. Since your action (associating user with existing client) is quite far from a standard set of RESTful actions (the closest variant will be update though), you need to introduce new action for your resource - for example assign_client. You can do it like this:

resources :users do
  resources :clients
  member do
    put :assign_client
  end
end

You can render form in any action you like. However, if you want something more standalone you can create a pair of actions (something like edit and update pair). The first one will render an appropriate form and the second one will process that form on the server.

查看更多
登录 后发表回答