I'm trying to write a nested form in ROR.
I have two tables Employee and EmployeeInfo and both table have a column named employeeID
these tables are connected with this key.
What i want to do is to create a form with some input fields which should update the values into both tables.
for eg i want a form which can create or update fields named employee_name, age, address and city But employee_name and age are present in table Employee and city and address are present in table EmployeeInfo.
So how should i write the form tag inorder to do this.
Please be sorry if question is a blunder. I'm realy new to this. Pls help
Extending #emu's answer
Models setup
#employee.rb
Class Employee < ActiveRecord::Base
has_one :employe_info
accepts_nested_attributes_for :employee_info
end
#employee_info.rb
Class EmployeeInfo < ActiveRecord::Base
belongs_to :employee
end
Controller
Class EmployeesController < ApplicationController
def new
#employee = Employee.new
#employee.build_employee_info
end
def create
#employee = Employee.new(employee_params)
if #employee.save
redirect_to #employee
else
render 'new'
end
end
private
def employee_params
params.require(:employee).permit(:employee_name, :age, employee_info_attributes: [:id, :city,:address])
end
end
In rails 4 you need to use
accepts_nested_attributes_for :employeeinfo
in your employee model. And also employee has the relation with emplyeeinfo is has_one.
in the form:
<%= form_for #employe, :html => { :multipart => true } do |f| %>
<% if #employe.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#employe.errors.count, "error") %> prohibited this employe from being saved:</h2>
<ul>
<% #employe.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :employee_name %><br>
<%= f.text_field :employee_name %>
</div>
<div class="field">
<%= f.label :age %><br>
<%= f.text_field :age %>
</div>
<%= f.fields_for :employeeinfo do |s| %>
<%= s.label :address %><br>
<%= s.text_field :address %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Please edit the form objects according to your model name.
Foriegn Key
Firstly the "connector" key you're referring to is called a foreign_key.
This is a standard relational database feature, allowing you to "join" two or more databases together with a single reference point. Whenever you use associations in Rails, you'll basically have to join the two datatables with a foreign_key
both table have a column named employeeID
Your employee_infos table only needs to have the foreign_key employee_id:
#app/models/employee.rb
Class Employee < ActiveRecord::Base
has_one :employee_info #-> foreign key = "employee_id"
accepts_nested_attributes_for :employee_info
end
#app/models/employee_info.rb
Class EmployeeInfo < ActiveRecord::Base
belongs_to :employee
end
Forms
As mentioned by Emu, you'll need to use accepts_nested_attributes_for
This allows you to pass data from a "parent" model to a child model, exactly what you have set up currently. The difference being you have to ensure you have it set up correctly.
Regardless of what you think, this is how you need to do it. You mention yourself that you're very new to Rails; which means your objections are likely based on your current DB setup. This might not be correct
You should use emu & Pavan's answers to fix this :)
Related
I'm guessing this is more of a fundamental issue than the form simply "not rendering", but here it goes. I'll try to keep this brief but a fair amount of context may be needed.
I'm making a custom Rolodex app and the organization gave me specific things to include. For this specific problem I'm dealing with contact emails only. Ideally I would have a system like Google Contact's, where you can click to add another email field and there's a dropdown to select a category (Home, Work, etc.).
In this case the categories are coming from a table called categories. Here is a link to the entity relationship diagram I made for the entire project (not just emails): http://i.imgur.com/LNSWZHy.jpg
To sum things up: How do I set things up to allow the entry of emails during a contact creation/edit?
Here's my relevant code:
models/contact.rb
class Contact < ActiveRecord::Base
has_many :emails
accepts_nested_attributes_for :emails
end
models/email.rb
class Email < ActiveRecord::Base
belongs_to :contact
belongs_to :category
end
controllers/contacts_controller.rb
# GET /contacts/new
def new
#contact = Contact.new
#email = #contact.emails.build(params[:email])
end
views/contacts/_form.html.erb
<%= form_for(#contact) do |f| %>
#Other contact fields here
<% f.fields_for #email do |email| %>
<div class="field">
<%= email.label :category_id %><br>
<%= email.text_field :category_id %><br/>
</div>
<div class="field">
<%= email.label :email %><br>
<%= email.text_field :email %><br/>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I also confirmed that this whole setup works "manually". I can make contacts and categories, and then properly reference them when creating a new email by manually putting in the foreign ids. The issue here is a matter of condensing this process into one form.
Any input would be appreciated, thanks!
Change:
<% f.fields_for #email do |email| %>
into
<%= f.fields_for #email do |email| %>
I fail to find the correct syntax to retrieve associated information. I have a model 'companyaccount' that belongs to a model 'company'.
class Company < ActiveRecord::base
has_many :companyaccounts
end
class Companyaccount < ActiveRecord::base
belongs_to :company
end
In the View, I can easily retrieve both the 'Companyaccount' 'number' and the 'id' of the associated company through the 'company_id' field of the 'companyaccount' table.
<%= #companyaccount.each do |companyaccount| %>
<%= companyaccount.company_id %>
<%= companyaccount.number %>
<% end %>
The View is called from this basic Controller
def index
#companyaccount = Companyaccount.all
end
Now, I do not want to have the company_id but the actual name of the company (housed in the 'company' table). I manage to do it for one specific company using a controller like this
def index
#companyaccount = Companyaccount.all
#company = Company.first
end
and then in the view change
<%= companyaccount.company_id %>
by
<%= company.name %>
but of course, this will retrieve, for each companyaccount, the same first company and not each of the company associated with the companyaccount. Could anyone provide me with the appropriate syntax to retrieve the associated company's name for each companyaccount in the "each" loop of my View? Thanks.
You can use delegate for this.
class Companyaccount < ActiveRecord::base
belongs_to :company
delegate :name, to: :company, prefix: true
end
and then you can call <%= companyaccount.company_name %>
You should be able to achieve what you want with this loop:
<%= #companyaccount.each do |companyaccount| %>
<%= companyaccount.company.name %>
<%= companyaccount.number %>
<% end %>
def index
#companyaccount = Companyaccount.all
end
and in view
<% #companyaccount.each do |companyaccount| %>
<%= companyaccount.company.name %>
<% end %>
for less db call you can change controller like
#companyaccount = Companyaccount.includes(:company).all
I recently had a problem getting checkboxes to work for a has_and_belongs_to_many (HABTM) association in Rails 4. I was able to find the information on how to get it working correctly in a few disparate places, but thought it would be good to document the few simple steps necessary to get it working correctly in one place here on StackOverflow.
As a setup assume a model of Kennel with a HABTM association to Handler.
class Kennel
has_and_belongs_to_many :handlers
end
This is all you need to do for the form: Don't do it manually when there is a built in helper.
<%= form_for #kennel do |f| %>
<%= f.collection_check_boxes(:handler_ids, Handler.all, :id, :to_s) %>
<% end %>
The form should have something like this:
<%= form_for(#kennel) do |form| %>
...
<div class="field">
<div class="field_head">Handlers</div>
<%= hidden_field_tag("kennel[handler_ids][]", nil) %>
<% Handler.order(:name).each do |handler| %>
<label><%= check_box_tag("kennel[handler_ids][]", id, id.in?(#kennel.handlers.collect(&:id))) %> <%= handler.name %></label>
<% end %>
</div>
...
<% end %>
The hidden_field_tag allows the user to uncheck all the boxes and successfully remove all the associations.
The controller needs to allow the parameter through strong parameters in the permitted_params method:
params.permit(kennel: [:city, :state
{handler_ids: []},
:description, ...
])
References:
http://railscasts.com/episodes/17-habtm-checkboxes
https://coderwall.com/p/_1oejq
I implement has_and_belongs_to_many association this way:
model/role
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
model/user
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
end
users/_form.html.erb
---
----
-----
<div class="field">
<% for role in Role.all %>
<div>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%= role.name %>
</div>
<% end %>
</div>
users_controller.rb
def user_params
params.require(:user).permit(:name, :email, { role_ids:[] })
end
Intermediate table_name should be roles_users and there should be two fields:
role_id
user_id
I have 3 text_fields in my view in which I enter students name. Of course you can enter one student or three students but I want to make sure that at least one student was provided because a project must have a student assigned to it.
Here is my view:
<%= form_for #project, url: projects_path do |f| %>
<p>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
</p>
<p>
<%= f.fields_for :students do |s| %>
<%= s.label :name %>
<%= s.text_field :name %>
<% end %>
</p>
<p>
<%= f.submit "Submit" %>
</p>
<% end %>
And new method from Projects controller:
def new
#project = Project.new()
3.times do
student = #project.students.build
end
end
What I want to achieve is to check if at least one student was provided and if not just show alert or disable submiting.
Edit
Models used in this project:
class Student < ActiveRecord::Base
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :students
accepts_nested_attributes_for :students
validate :validate_student_count
def validate_student_count
errors.add(:students, "at least one is required") if students.count < 1
end
end
Lots of very similar questions on the internet. Here's some examples: Validate the number of has_many items in Ruby on Rails and Validate that an object has one or more associated objects
Just add a custom validation rule as:
validate :validate_student_count
def validate_student_count
errors.add(:students, "at least one is required") if students.count < 1
end
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