I have a form from which a project is created. The form collects data for two tables; 'projects' and 'projects_users'. The last one
is a relational tabel which is used for storing which users that are members of the project (choosen in the form).
Creating a new project works fine, but when it comes to creating new posts in 'projects_users' it fails:
uninitialized constant ProjectsController::Projects_users
First of all, am I thinking in the right way (see the code below)?
What I do is that I extract the members array from params[:project], that's retrieved from the form. Then I'm creating the project in the db, and when that is done I iterate through the members array (which contains of user_id's) and creates the posts in the db for 'projects_users'.
What am I doing wrong? Is there a better way to achieve what I want?
projects controller:
class ProjectsController < ApplicationController
def new
#project = Project.new
#users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id]))
end
def create
#members = params[:project].delete(:members)
#project = Project.new(params[:project].merge(:user_id => current_user.id))
if #project.save
#members.each do |member|
#project_members = Projects_users.new
#project_members.project_id = #project.project_id
#project_members.user_id = member.user_id
end
redirect_to #project
else
#users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id]))
render :new
end
end
end
new.html.erb:
<%= form_for #project do |f| %>
<div class="alert alert-block">
<%= f.error_messages %>
</div>
<div class="text_field">
<%= f.label :title%>
<%= f.text_field :title%>
</div>
<div class="text_field">
<%= f.label :description%>
<%= f.text_field :description%>
</div>
<div class="dropdown">
<%= f.label :start_date%>
<%= f.date_select :start_date %>
</div>
<div class="dropdown">
<%= f.label :end_date%>
<%= f.date_select :end_date %>
</div><br/>
<span class="help-block">Välj användare som ska ingå ingå i projektet.</span>
<div class="checkbox">
<% #users.each do |user| %>
<%= check_box_tag "project[members][]", user.id, '1', :id => "user_#{user.id}" %>
<%= label_tag "user_#{user.id}", user.first_name + ' ' + user.last_name, :class => "checkbox" %>
<% end %>
</div>
</div>
<div class="submit">
<%= f.submit "Spara" %>
</div>
<% end %>
project model:
class Project < ActiveRecord::Base
has_and_belongs_to_many :users, :class_name => 'User'
belongs_to :user
has_many :tickets, :dependent => :destroy
// validation...
# Setup accessible (or protected) attributes for your model
attr_accessible :user_id, :title, :description, :start_date, :end_date
end
users model:
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email, :password
has_and_belongs_to_many :projects
has_many :tickets
... other code
end
projects_users table:
project_id
user_id
projects table:
user_id (the user that creates the project = admin)
title
description
start_date
end_date
In the controller you are using Projects_users.new that means the new method for Projects_users model and as you are using HABTM association the join model doesn't exist. If you want to use the join/associated model then you must use has_many :through association. But in case you just want to associate preexisting users to projects you can do it following way
#project.user_ids = #members
OR
#members.each do |member|
user = User.find(member)
#project.users << user
end
#member will be an array in your case not a hash. It will contain all the checked users' ids. So you don't need to have member.user_id as it is not a hash.
Related
I have three models, companies, transactions and subtransactions. Each has_many of the next. I need to build a form that saves to both transactions and multiple records of subtransactions.
I'm struggling with the best approach to the form and the appropriate way to save to the DB. Ideally, the records get saved at once if all pass validation.
My simplified form:
<%= form_for(#transaction) do |f| %>
<div class="field" id="transaction_amount">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<% 1.upto(5) do |i| %>
<%= fields_for("subtransaction[#{i}]") do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal, "Subtotal #{i}" %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
<% end %>
My TransactionsController:
def new
#transaction = company.transactions.build if logged_in?
end
def create
#transaction = company.transactions.build(transaction_params)
params[:subtransaction].each do |i|
# build and save
end
if #transaction.save ## #subtransaction.save
flash[:success] = "Success!"
redirect_to success_url
else
render :new
end
end
The way to do this is to pass the nested attributes through the various models:
#app/models/company.rb
class Company < ActiveRecord::Base
has_many :transactions
accepts_nested_attributes_for :transactions
end
#app/models/transaction.rb
class Transaction < ActiveRecord::Base
has_many :sub_transactions
accepts_nested_attributes_for :sub_transactions
end
This will give you the ability to use the following:
#app/controllers/transactions_controller.rb
class TransactionsController < ApplicationController
def new
#transaction = company.transactions.new
5.times do
#transaction.sub_transactions.build
end
end
def create
#transaction = company.transactions.new transaction_params
#transaction.save
end
private
def transaction_params
params.require(:transaction).permit(sub_transactions_attributes: [:subtotal])
end
end
Then...
#app/views/transactions/new.html.erb
<%= form_for #transaction do |f| %>
<%= f.fields_for :sub_transactions do |s| %>
<%= s.text_field :subtotal %>
<% end %>
<%= f.submit %>
<% end %>
This will allow you to save the various nested attributes through the main object. It is the correct, conventional, way to do it.
model
class Transaction < ActiveRecord::Base
has_many :subtransations
accepts_nested_attributes_for :subtransactions
end
controller
def new
#transaction = Transaction.new
#transaction.subtransactions.build
end
def create
company = Company.first # Or whatever you need.
# We merge the company id to the transaction params since
# a transaction belongs_to a company.
#transaction = Transaction.create!(create_params.merge(company_id: company.id))
end
private
def create_params
params.require(:transaction).permit(
:total,
subtransactions_attributes: [:id, :subtotal]
)
end
view
<%= form_for(#transaction) do |f| %>
<div class="field">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<%= f.fields_for :subtransaction do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
This will allow to create only one subtransaction with the transaction record
If you want to create more than one subtransaction record you should use cocoon gem, it's easy to setup and will save much precious time.
I have tried all of the solutions to similar problems and haven't gotten this one figured out.
I have a has_many :through relationship between 'Clinician', and 'Patient' with a joined model 'CareGroupAssignment'. None of the methods I have tried so far been able to save the clinician to patient association. I would like to have a patient be able to have multiple clinicians associated with it and clinicians will have multiple patients.
clinician.rb (simplified)
class Clinician < ActiveRecord::Base
belongs_to :care_group
has_many :patients ,:through=> :care_group_assignments
has_many :care_group_assignments, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :user, :allow_destroy => true
end
patient.rb
class Patient < ActiveRecord::Base
belongs_to :care_group
has_many :clinicians ,:through=> :care_group_assignments
has_many :care_group_assignments
belongs_to :user
accepts_nested_attributes_for :user, :allow_destroy => true
end
care_group_assignments.rb
class CareGroupAssignment < ActiveRecord::Base
belongs_to :clinician
belongs_to :patient
end
I first tried to follow the example from Railscasts PRO #17- HABTM Checkboxes to at least start getting the data collected and to have the models set up correctly. Below is the form with the checkboxes for each clinician as described in the RailsCast, checkboxes show up and the data is sent but not stored (can't figure out why).
patient new.html.erb form
<%= form_for #patient do |form| %>
<%= form.fields_for :user do |builder| %>
<div class="form-group">
<%= builder.label "Email or Username" %>
<%= builder.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= builder.label :password %>
<%= builder.password_field :password, class: "form-control" %>
</div>
<% end %>
<div class="form-group">
<%= form.label :first_name %>
<%= form.text_field :first_name, class: "form-control", placeholder: "First name" %>
</div>
<div class="form-group">
<%= form.label :last_name %>
<%= form.text_field :last_name, class: "form-control", placeholder: "Last name" %>
</div>
<div class="form-group">
<% Clinician.where(care_group_id: #care_group.id).each do |clinician| %>
<%= check_box_tag "patient[clinician_ids][]", clinician.id, #patient.clinician_ids.include?(clinician.id), id: dom_id(clinician) %>
<%= label_tag dom_id(clinician), clinician.full_name %><br>
<% end %>
</div>
<%= form.button 'Create Patient', class: "btn btn-u btn-success" %>
<% end %>
Next, I tried the collection_select answer to this question. This creates a badly formatted list where only one clinician can be selected. The data seems to get sent but again doesn't save.
patient new.html.erb form
<div class="form-group">
<%= collection_select(:patient, :clinician_ids,
Clinician.where(care_group_id: #care_group.id).order("first_name asc"),
:id, :full_name, {:selected => #patient.clinician_ids, :include_blank => true}, {:multiple => true}) %>
</div>
Lastly, I copied what was done in this questions/solution. Also isn't formatted as a normal collection_select dropdown but instead a list with a boarder around it where only one clinician can be selected.
patient new.html.erb form
<div class="form-group">
<% Clinician.where(care_group_id: #care_group.id).each do |clinician| %>
<%= check_box_tag "patient[clinician_ids][]", clinician.id, #patient.clinician_ids.include?(clinician.id), id: dom_id(clinician) %>
<%= label_tag dom_id(clinician), clinician.full_name %><br>
<% end %>
</div>
None of these methods have so far been able to save the clinician to patient association.
patient_controller.rb
def new
#patient = Patient.new
#user = User.new
#patient.build_user
#care_group = current_clinician.care_group
end
def create
#patient = Patient.create(patient_params)
#patient.care_group = current_clinician.care_group
if #patient.save
redirect_to patient_path(#patient), notice: "New patient created!"
else
render "new"
end
end
def show
#patient = Patient.find_by(id: params["id"])
end
private
def patient_params
params.require(:patient).permit({:clinician_ids => [:id]},:first_name,:last_name,:user_id,:care_group_id, user_attributes: [ :email, :password, :patient_id, :clinician_id ])
end
I plan to display the clinicians associated with a patient on the patient show page:
patient show.html.erb
<strong>Shared with:</strong>
<% #patient.clinicians.each do |clinician| %>
<%= clinician.full_name %><
<% end %>
This works if I seed the database but since the data doesn't seem to be stored, nothing is showing up.
Rails 4.1.8, ruby 2.2.1p85, PostgreSQL
Thanks
Found the answer on another question I asked:
problem is this line in the controller:
params.require(:patient).permit({:clinician_ids => [:id]}...
It should be:
params.require(:patient).permit({:clinician_ids => []}...
I have a Lesson model and a Revision model. When a lesson is created its first revision is created (the models have a has_many through: relation). Users should be able to create new revisions by updating the old one - but the updates would save to a new revision.
Currently my lessons and the first revision version save properly, through a nested form. What I want to know is - how can I create a "new revision" form that does what the update form does, but save the results to a new revision? (The lesson the new revision is tied to would be unchanged.)
EDIT: With my updated (and hopefully more concise) code below, new revisions save, but I never get directed to a form. I'm sent straight to the profile page where I can see the list of revisions. How can I make updates rather than just a duplicate? I've been reading http://guides.rubyonrails.org/form_helpers.html, but am clearly still missing a step.
The entire app is on https://github.com/arilaen/pen.
Revision Controller
class RevisionsController < ApplicationController
def new
#revision = Revision.new
#lesson = Lesson.find(params[:lesson_id])
end
def create
#revision = Revision.new(params[:revision])
#revision.user_id = current_user.id
#revision.time_updated = DateTime.now
#revision.save
redirect_to current_user.profile
end
def show
#revision = Revision.find(params[:id])
end
def edit
#old_revision= Revision.find(params[:id])
#revision = Revision.new(params[:revision])
#revision.user_id = current_user.id
#revision.lesson_id = #old_revision.lesson_id
#revision.time_updated = DateTime.now
#revision.save
redirect_to current_user.profile
end
end
Revision Model:
class Revision < ActiveRecord::Base
attr_accessible :comment, :lesson_id, :user_id, :description, :content, :time_updated, :old_revision_id
belongs_to :lesson, :class_name => "Lesson", :foreign_key => "lesson_id"
belongs_to :user, :class_name => "User", :foreign_key => "user_id"
accepts_nested_attributes_for :lesson
end
New Revision Form
edit.html.erb
<%= form_for #revision do |f| %>
<%= render "form" %>
<% end %>
_form.html.erb in revisions
<%= form_for #revision :url => { :action => "create" } do |f| %>
<div class="field">
<%= f.label :description %><br />
<%= f.text_field :description, :value => #old_revision.description %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :value => #old_revision.content %>
</div>
<div class="field">
<%= f.label :comment %><br />
<%= f.text_field :comment, :value => #old_revision.comment %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Excerpt from Revision/show:
<% if current_user.nil? %>
<% else %>
<% if #revision.user_id = current_user.id %>
<%= link_to 'New Revision', edit_lesson_revision_path(#revision.id) %><br />
<% else %>
<%= link_to 'Copy', edit_lesson_revision_path(#revision.id) %>
<% end %>
<% end %>
Here's my lessons model because it was requested earlier, may or may not be relevant to solution:
class Lesson < ActiveRecord::Base
attr_accessible :stable, :summary, :title, :time_created, :revision, :revisions_attributes
has_many :revisions, :class_name => "Revision"
has_many :users, :through => :revisions
accepts_nested_attributes_for :revisions
end
routes.rb
resources :revisions
resources :lessons do
resources :revisions
end
I am using the rails3-jquery-autocomplete gem and it works neat when using it for fields that exist in the model. However, I have been trying to use it for associations and I fail to see how to make it work. So let's explain what I have:
Models:
class Receipt < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :receipts
end
Controller
class Admin::ReceiptsController < AdminController
autocomplete :user, :name
def index
#receipts = Receipt.all
end
def show
#receipt = Receipt.find_by_id(params[:id])
end
def edit
#receipt = Receipt.find_by_id(params[:id])
#users = User.all
end
end
View (form):
<%= form_for(#receipt, :url => admin_receipt_path, :html => { :multipart => true }) do |f| %>
<div class="clearfix">
<%= f.label :value, "Value($)" %>
<div class="input"><%= f.text_field :value %></div>
</div>
<div class="clearfix">
<%= f.label :user_id, "User" %>
<div class="input">
<%= f.autocomplete_field :user_id, autocomplete_user_name_admin_receipts_path %>
</div>
</div>
.....
The thing is... I am able to fetch user names, but I want to actually store the user id in there. The same way I would like to show the name when the admin tries to edit an existing receipt with a user associated. Something that I am able to do with this drop down:
<div class="clearfix">
<%= f.label :user_id, "User" %>
<div class="input"><%= f.select :user_id, #users.collect {|p| [ p.name, p.id ] },{:prompt => 'Select a User'} %></div>
</div>
I am failing to see how would I do this with this gem....
Ryan Bates did a screencast on this. Clean and well explained
Screencast
If you don't feel like subscribing try torrent
I've been following through these Railscasts and trying to amend the code so it works with Rails 3:
http://railscasts.com/episodes/73-complex-forms-part-1
http://railscasts.com/episodes/73-complex-forms-part-2
http://railscasts.com/episodes/73-complex-forms-part-3
I am trying to create Groups, Users and Memberships (the many-to-many relationships) simultaneously. People can add users to the group as they create it and then I want it to route through to a view of the group with all the members. I can get memberships to create just fine but having trouble creating users and associating them the group. My code currently looks like this:
Group.rb
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
attr_accessible :group_name
def user_attributes=(user_attributes)
user_attributes.each do |attributes|
users.build(attributes)
end
end
end
Membership.rb
class Membership < ActiveRecord::Base
belongs_to :groups
belongs_to :users
end
User.rb
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
end
groups_controller.rb
class GroupsController < ApplicationController
def create #todo test for number of groups they're already in before creation
#group = Group.new(params[:group])
#group.memberships.build(:user_id => current_user.id)
##group..build
respond_to do |format|
if #group.save
format.html { redirect_to(#group, :notice => 'Group was successfully created and user added...?') }
else
format.html { render :action => "new" }
end
end
end
end
My form looks like this:
Which is created by:
views/groups/new.html.rb
<h1>New group</h1>
<%= form_for(#group) do |f| %>
<fieldset>
<legend>Create a new group</legend>
<%= render 'shared/group_error_messages', :object => f.object %>
<div class="clearfix">
<%= f.label :group_name %>
<div class="input">
<%= f.text_field :group_name %>
</div>
</div>
<div id="users">
<%= render :partial => 'user', :collection => #group.users %>
</div>
</div>
<p><%= add_user_link "Add a member" %></p>
<div class="actions">
<%= f.submit :value => 'Create your group', :class => 'btn success'%>
<!-- todo test for if they already have a group...-->
</div>
</fieldset>
<% end %>
views/groups/_user.html.rb
<div class="user">
<%= fields_for :user do |f| %>
<div class="clearfix">
<%= f.label :name %>
<div class="input">
<%= f.text_field :name %>
</div>
</div>
<div class="clearfix">
<%= f.label :number %>
<div class="input">
<%= f.text_field :number %>
</div>
</div>
<%= link_to_function "remove", "$(this).up('.user').remove()" %>
<% end %>
</div>
Thanks very, very much in advance :)
You stumbled upon a fairly out of date Railscast it seems. Some age very well... this one not as much. What you're looking for is a accepts_nested_attributes_for which was introduced in rails 2.3.
Group model:
#app/models/group.rb
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
accepts_nested_atrributes_for :users
end
Group Controller:
#app/controllers/groups_controller.rb
Class GroupsController < ApplicationController
def create
#group = Group.new(params[:group)
#group.save ? redirect_to(#group) : render("new")
end
end
New Group action:
<!-- Giving you the important changes only -->
<div id="users">
<%= render :partial => 'user', :collection => #group.users, :locals => { :form => f } %>
</div>
User partial:
<div class="user">
<%= form.fields_for :user do |f| %>
As a general rule of thumb, you should use more descriptive names for your form block variables, especially with nested forms.