Help with rails nested forms - ruby-on-rails

I have these three models.
Student.
Evaluation.
Grade (student_id, evaluatioin_id, value)
I want to make a form so that a User can set the grade for each student of a evaluation...
I want to keep this is as clean as possible (and restful)...
Im open to any suggestions on how to get this done.
Please Help.

See these
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2

I suppose you have the following associations:
Evaluation:
class Evaluation < ActiveRecord:Base
has_many :grades
accepts_nested_attributes_for :grades
end
Student:
class Student < ActiveRecord::Base
has_many :grades
end
Grade:
class Grade < ActiveRecord::Base
belongs_to :student
blongs_to :evaluation
end
Regarding your comment I think you want to use the nested_form_for gem made by Ryan Bates. It helps you add/delete dynamically nested attributes in your form, very efficiently. You can use it like this:
<% nested_form_for #evaluation do |f| %>
<% f.fields_for :grades do |gf| %>
<%= gf.label 'Username of the student' %>
<%= gf.collection_select(:student, :student_id, Student.All, :id, :username) %> <br/>
<%= gf.label :value %>
<%= gf.text_field :value %> <br/>
<%= gf.link_to_remove 'delete' %> <br/>
<% end %>
<%= f.link_to_add 'Add grade', :grades %> <br/>
<%= f.submit %>
<% end %>
Tell me if it works/fits your needs. I have been using this gem a lot these previous days.

What I ended up doing was..
In Grade Model
scope :for_student,lambda{|student, evaluation| where("student_id"=>student.id).where("evaluation_id"=>evaluation.id)}
In EvaluationsController
def assign_grades])
#evaluation = Evaluation.find(params[:id])
#students = Student.all
#students.each do |s|
grade = Grade.for_student(s,#evaluation)
if !grade.exists?
#evaluation.grades.build(:value=> 0,:student_id=>s.id)
end
end
end
def save_grades
#evaluation = Evaluation.find(params[:id])
#evaluation.update_attributes(params[:evaluation])
redirect_to [#evaluation.batch_subject, #evaluation]
end
In View
<table class="medium">
<thead>
<tr>
<th>name</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<%=f.fields_for :grades do |g|%>
<tr>
<td><%=g.label :value,g.object.student.full_name%> <%= g.hidden_field :student_id%></td>
<td><%=g.text_field :value%></td>
</tr>
<%end%>
</tbody>
</table>

Related

Edit multiple saved records

I'm trying to develop a simple exercise log app, and I'm stuck (again). A user selects a category, and a specific workout, and I return a list of exercises belonging to that workout.
An effort is a user specific instance of an exercise.
class Exercise < ActiveRecord::Base
has_many :efforts
end
class Effort < ActiveRecord::Base
belongs_to :exercise
end
I'm retrieving the list of exercises in the efforts controller
class EffortsController < ApplicationController
def log_workout
#exercises = Exercise.where("category_id = ? AND workout_id = ?", params[:exercise][:category_id], params[:exercise][:workout_id])
#effort = Effort.new
end
end
The user logs their workout by using the following form
<%= form_tag save_workout_path, method: :put do %>
<table>
<tr>
<th>Exercise</th>
<th>Sets</th>
<th>Reps</th>
</tr>
<% #exercises.each do |exercise| %>
<%= fields_for "efforts[]", #effort do |f| %>
<tr>
<td><%= exercise.name %></td>
<td><%= f.number_field :sets %></td>
<td><%= f.number_field :reps %></td>
<%= f.hidden_field :exercise_id, :value => exercise.id %>
</tr>
<% end %>
<% end %>
</table>
<%= submit_tag "Log it" %>
<% end %>
class EffortsController < ApplicationController
def create
params[:efforts].each do |values|
if current_user.efforts.create(values)
flash[:notice] = 'Efforts were successfully saved.'
else
render action: 'new'
end
end
end
end
How right this is, I'm not sure, but it works. My problem now is trying to retrieve saved 'efforts' and allowing them to be modified through the same form. (Letting the user edit their workout log). Would appreciate any guidance/help

Accessing data from tables

I'm trying to reference a column from another table in my rails application. I want to reference the site_name using the matching site_id columns from both tables(sites and trials).
My controller
def list
#list = Trial.year(params[:year]).order(:region_id)
end
My Models
class Trial < ActiveRecord::Base
attr_accessible :trial_id, :site_id, :year, :trial_type, :region_id
scope :year, ->(year) { where(year: year) }
scope :trial_id, ->(trial_id) { where(trial_id: trial_id) }
belongs_to :site
end
class Site < ActiveRecord::Base
attr_accessible :site_id, :site_name, :region
has_many :trials
end
My View
<table class="table">
<th>Trial Location</th>
<th>Trial Type</th>
<th>Grower</th>
<% #list.each do |list| %>
<tr>
<td>
<%= link_to list.site.site_name, trial_trials_path(trial_id: list.trial_id) %>
</td>
<td>
<%= link_to list.trial_type, trial_trials_path(trial_id: list.trial_id) unless list.trial_type.blank? %>
</td>
<td>
<%= link_to list.trial_type, trial_trials_path(trial_id: list.trial_id) unless list.trial_type.blank? %>
</td>
</tr>
<% end %>
</table>
Setting up the association in your model does write the getter method for you. The attr_accessibles sets up that method.
Add :site to your Trial's attr_accessible

Rails has_many checkboxes with extra attribtues

We have the following code working for a complex rails form with checkboxes. I'm not really happy with the solution we have in place and I was wondering if anyone knows of a more proper way to do this in rails. All the code below is working I just want to know if there is a cleaner approach.
In my Admins controller I want to remove the need to call the following code on each update.
#user.admin.school_admin_roles.destroy_all
params[:roles].each do |school_role|
ids = school_role.split('_')
#user.admin.school_admin_roles.find_or_create_by_school_id_and_school_role_id(ids[0], ids[1])
end if !params[:roles].nil?
So I basically want to be able to call #user.update_attributes(params[:user]) and have rails take care of creating the needed relationships for me. I have that working with AccountRole in the form below. I want to know if there is a way to do the same thing with SchoolRole given I have an extra variable school_id in the join table.
We have the following form for editing a user and assigning roles
Screenshot of form ->
http://i.stack.imgur.com/PJwbf.png
I have the following form where an admin can edit other users and assign account based roles and school based roles via checkboxes. The account based roles were easy to implement. The school based rules are a bit complicated since the join table school_admin_roles has school_id, user_id, role_id fields. We had to implement the school roles part of the form in a rather hackish way. We have the form implemented like this - notice how we hacked together school.id.to_s+'_'+role.id.to_s into the same checkbox on school roles.
In the Admins controller's update function we manually destroy all school_admin roles on each update then loop through the school roles params do a split on the ids on '-' then manually re-create each school based role. I really hate the way we've had to go about this. Could anyone shed some light on a cleaner more rails centric approach to solving this scenario?
The form -
<%= form_for #user, :url => {:controller => 'admins', :action => 'update'} do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.fields_for :admin do |uf| %>
<div class="field">
<%= uf.label :first_name %>
<%= uf.text_field :first_name %>
</div>
<label>Admin Permissions</label>
#account level permissions works fine
<%= hidden_field_tag "#{uf.object_name}[account_role_ids][]" %>
<% AccountRole.find(:all).each do |role| %>
<div class="account_role">
<%= check_box_tag "#{uf.object_name}[account_role_ids][]", role.id, #user.admin.account_roles.include?(role)%>
<%= role.name %>
</div>
<% end %>
#school level permissions a bit of a hack
<%= hidden_field_tag "#{uf.object_name}[school_role_ids][]" %>
<% SchoolRole.find(:all).each_with_index do |role, index| %>
<div class="school_role">
<%= check_box_tag "#{uf.object_name}[school_role_ids][]",role.id, #user.admin.school_roles.include?(role) %>
<%= role.name %>
<span class="advanced_box admin_permissions" <% if #user.admin.school_roles.include?(role) %>style="display:inline"<% end %>>
<div class="content" id="perm_<%= index %>">
<h4><%= role.name %></h4>
<% uf.object.account.schools.each do |school|%>
<div>
<%= check_box_tag "roles[]", school.id.to_s+'_'+role.id.to_s, role.school_admin_roles.where(:admin_id => uf.object.id).collect(&:school_id).include?(school.id)%>
<%= school.name %>
</div>
<% end %>
<%= link_to 'Done', '#', :class => "done" %>
</div>
Advanced
</span>
</div>
<% end %>
</div>
<% end %>
The controller
class AdminsController < ApplicationController
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
# TODO find a way to refactor this
#user.admin.school_admin_roles.destroy_all
params[:roles].each do |school_role|
ids = school_role.split('_')
#user.admin.school_admin_roles.find_or_create_by_school_id_and_school_role_id(ids[0], ids[1])
end if !params[:roles].nil?
#
flash[:notice] = "Successfully updated Admin."
redirect_to admins_path
else
render "edit"
end
end
end
Given the following models
class User < ActiveRecord::Base
has_one :parent
has_one :admin
has_many :scool_admin_roles
has_many :account_admin_roles
end
class AccountAdminRole < ActiveRecord::Base
before_save :set_account_id
belongs_to :admin
belongs_to :account_role
end
class SchoolAdminRole < ActiveRecord::Base
belongs_to :admin
belongs_to :school_role
belongs_to :school
end
class SchoolRole < ActiveRecord::Base
has_many :school_admin_roles
end
class AccountRole < ActiveRecord::Base
has_many :account_admin_role
end
When I face code that I know smells bad, usually it leads me to the design.
In this case, the problem is the database table design.
You are hacking the value passed from a checkbox with a delimiter because the "join" table does more than just join. I believe that the relationship to school belongs_to the SchoolRole and not the SchoolAdminRole. Changing this will create a pattern much like your AccountRole.
Correcting the model design, might be a bit painful now, but it is much cleaner and will be maintainable in the future. You will thank yourself later.
We refactored the code above as follows
In the model we added accepts_nested_attributes_for :school_admin_roles, :reject_if => proc { |attr| attr['school_role_id'].blank? }
and added school_admin_roles_attributes to attr_accessible
class Admin < ActiveRecord::Base
belongs_to :account
belongs_to :user
has_many :school_admin_roles
has_many :school_roles, :through => :school_admin_roles
has_many :account_admin_roles
has_many :account_roles, :through => :account_admin_roles
accepts_nested_attributes_for :account
accepts_nested_attributes_for :school_admin_roles, :reject_if => proc { |attr| attr['school_role_id'].blank? }
attr_accessible :account_role_ids, :email, :first_name, :last_name, :account_id, :user_id, :account_attributes, :school_admin_roles_attributes
default_scope where(:deleted => false)
end
We then built the form as follows
<% index2 = 0 %>
<% SchoolRole.find(:all).each_with_index do |role, index| %>
<div class="school_role">
<%= check_box_tag "school_roles[]",role.id, #user.admin.school_roles.include?(role) %>
<%= role.name %>
<span class="advanced_box admin_permissions" <% if #user.admin.school_roles.include?(role) %>style="display:inline"<% end %>>
div class="content" id="perm_<%= index %>">
<h4><%= role.name %></h4>
<% uf.object.account.schools.each do |school|%>
<div>
<%= check_box_tag "#{uf.object_name}[school_admin_roles_attributes][#{index2}][school_role_id]", role.id, role.school_admin_roles.where(:admin_id => uf.object.id).collect(&:school_id).include?(school.id)%>
<%= school.name %>
<%= hidden_field_tag "#{uf.object_name}[school_admin_roles_attributes][#{index2}][school_id]", school.id %>
</div>
<% index2 += 1 %>
<% end %>
<%= link_to 'Done', '#', :class => "done" %>
</div>
Advanced
</span>
</div>
<% end %>
</div>
<% end %>
Which then enabled us to refactor the controller without splitting the ids but we still have to call destroy all each time which I can live with.
def update
#user = User.find(params[:id])
#user.admin.school_admin_roles.destroy_all
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated Admin."
redirect_to admins_path
else
render "edit"
end
end

uninitialized constant Reservations

hi im having a bit trouble in my application im new in ROR development
i made a static page where my function rooms of my reservation
application are showed and can be add in in the
reservation_function_room(line item of reservation functionroom) it
raises uninitialized constant Reservations when ever i route to <%= link_to "add functionrooms", reservation_pages_functionroom_path(#reservation) %> cant figure out whats wrong
very thanks in advance thanks
page-static pages
class PagesController < ApplicationController
def functionroom
#reservation = Reservation.find(params[:reservation_id])
#function_room = FunctionRoom.all
end
end
functionroom.html.erb
<% if notice %>
<p id = "notice"><%= notice%></p>
<%end%>
<h1>functionRooms</h1>
<%#function_room.each do |functionroom|%>
<h3><%= functionroom.name%></h3>
<p><%= number_to_currency(functionroom.price)%></p>
<%= button_to 'add function room',
reservation_reservation_function_room_path(:function_room_id =>
functionroom), :method => :post,:remote => true%>
<%end%>
reservation_contoller.rb
def index
#reservations = Reservation.all
end
def show
#reservation = Reservation.includes(:reservation_function_rooms =>
:function_room,:reservation_package => :package).find(params[:id])
end
class ReservationFunctionRoomsController < InheritedResources::Base
def show
#reservation_function_room =
ReservationFunctionRoom.find(params[:id])
end
def new
#reservation_function_room = ReservationFunctionRoom.new
end
def create
#reservation = Reservation.find(params[:reservation_id])
function_room = FunctionRoom.find(params[:function_room_id])
#reservation_function_room =
#reservation.add_function_room(function_room.id)
if #reservation_function_room.save
redirect_to #reservation, :notice => "function room successfuly
added"
end
end
end
routes
get "pages/menu"
resources :reservation_function_rooms
resources :services
resources :reservations do
get "pages/functionroom"
end
resources :reservation_packages
resources :package_line_items
resources :packages do
resources :package_crews
end
resources :function_rooms
reservation.rb
class Reservation < ActiveRecord::Base
has_one :reservation_package
belongs_to :service
has_many :reservation_function_rooms
has_many :package_line_items
has_many :menus , :through => :package_line_items, :uniq => true
has_many :function_rooms, :through =>:reservation_function_rooms
def add_function_room(function_room_id)
current_function_room =
reservation_function_rooms.find_by_function_room_id(function_room_id)
if current_function_room
redirect_to #reservation, :notice => "function room already
added"
else
current_function_room =
reservation_function_rooms.build(:function_room => function_room_id)
current_function_room.price =
current_function_room.function_room.price
end
current_function_room
end
end
reservation/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= #reservation.name %>
</p>
<%= display_package #reservation%>
<p>
<b>Address:</b>
<%= #reservation.address %>
</p>
<p>
<b>Contact:</b>
<%= #reservation.contact %>
</p>
<p>
<b>Date:</b>
<%= #reservation.date %>
</p>
<p>
<b>Timestart:</b>
<%= #reservation.timeStart %>
</p>
<p>
<b>Timeend:</b>
<%= #reservation.timeEnd %>
</p>
<p>
<b>Numguest:</b>
<%= #reservation.numGuest %>
</p>
<p>
<%= link_to "add functionrooms", reservation_pages_functionroom_path(#reservation) %>
</p>
<table>
<tr>
<th>Function room</th>
<th>Price</th>
</tr>
<% #reservation.reservation_function_rooms.each do |room|%>
<tr>
<td><%= room.function_room.name%></td>
<td><%= room.function_room.price%></td>
</tr>
<%end%>
</table>
<%= link_to 'Edit', edit_reservation_path(#reservation) %> |
<%= link_to 'Back', reservations_path %>
if anything is needed feel free to ask thanks in advance :)
I know it's been 4 years, but I ran through the same issue and I think I may have found where it comes from.
You have dependency "has_one :reservation_package"
and lower you have "resources :reservation_packages"
I think the "s" here is the problem : either do a single resource OR a has_many relation. That why he can't find it.
That's how i fixed my problem.

form_for for relation table with type of many to many relation

My goal is to display select box for each relation for users and specific project.
All users need to be listed but only project users have some type of relation. Other users have none selected in theirs select box.
I have this model:
class Project < ActiveRecord::Base
belongs_to :company
has_many :tasks, :order => 'state_type_id ASC'
has_many :project_user_relations
has_many :users, :through => :project_user_relations
def t_name
name.camelcase
end
end
class User < ActiveRecord::Base
belongs_to :company
has_many :tasks , :foreign_key => :assigned_user_id
has_many :project_user_relations
has_many :projects, :through => :project_user_relations
def full_name
firstname + ' ' + lastname
end
def relation_to(project)
relation=ProjectUserRelation.find_by_project_id_and_user_id(project.id, id)
relation ||= relation=ProjectUserRelation.new
end
end
class ProjectUserRelation < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_user_relation_type
end
class ProjectUserRelationType < ActiveRecord::Base
def t_name
I18n.t("app.projects.users.relation.type."+code)
end
end
I want make a form to display all users, with collection_select.
I used code:
def edit_all
#project = Project.find(params[:project_id])
#users = User.all
....
in my controler
routes works ok.
in my view:
<% #users.each do |user| %>
<%= f.fields_for :users, user do |user_fields| %>
<tr class="reference" rel="<%= parent_user_path(user) %>" >
<td class="name"><%= link_to user.full_name, parent_user_path(user) %></td>
<td class="email"><%= mail_to user.email %></td>
<td class="type">
<%= user_fields.fields_for user.relation_to #project do |relation_fields| %>
<%= relation_fields.collection_select :project_user_relation_type, ProjectUserRelationType.all, :id, :t_name, {:include_blank => false, :prompt => t("helpers.select.prompt") } %>
<% end %>
</td>
</tr>
<% end %>
<% end %>
or for test:
<%= f.fields_for :users, #users do |xuser_fields| %>
<% logger.debug "#{self.to_s} xuser_fields = #{xuser_fields.object.inspect} ;" %>
<tr>
<td><%= xuser_fields.text_field :firstname %></td>
<td></td>
<td></td>
<td></td>
</tr>
<% end %>
but notnihng woks right
first one generates wrong name in html:
select id="project_users_project_user_relation_project_user_relation_type" name="project[users][project_user_relation][project_user_relation_type]"
second one generates error:
undefined method `firstname' for # Array:0x4d03658
Can you help me to solve this situation.
PS:sorry for long code :(
SOLUTION (probably - solved by reading RoR sources)
I found sollution i thing.
A method
def name_attributes=(attributes)
# Process the attributes hash
end
in Project model was missing.
It is unbelievable sollution :].
There is also exact syntax after fields_for: :name, #some_collection, where name must be exactly same name as in the beginign of mentioned def in Model.

Resources