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 %>
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.
You've nested resources here. Like you mentioned in your comment,
users/:user_id/clients/new
andclients/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 beupdate
though), you need to introduce new action for your resource - for exampleassign_client
. You can do it like this: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
andupdate
pair). The first one will render an appropriate form and the second one will process that form on the server.