Rails retrieving associated information - ruby-on-rails

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

Related

Simple Form check box for join table relationship

I can't figure this out for the life of me but here are my models:
class User < ApplicationRecord
has_many :user_stores
has_many :stores, through: :user_stores
end
class UserStore < ApplicationRecord
belongs_to :user
belongs_to :store
end
class Store < ApplicationRecord
has_many :user_stores
has_many :users, through: :user_stores
end
So I have a join table, I'm trying to make a form, which would have selected checkboxes next to the store names that the user has selected (this information would come from the join table relationship) and open checkboxes for the remaining stores (coming from the Store model). How do I show that in the view/make it work in the controller as well. Would I use collections instead? ( I am using Devise and Simple Form gem )
This is what I have so far:
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.fields_for :stores, #user.stores do |s| %>
# not sure if this is the right way or not
<% end %>
<%= f.button :submit %>
<% end %>
Store Controller:
class StoresController < ApplicationController
...
def new
#user = current_user
#stores = Store.all
# #user.stores => shows user's stores (from join table)
end
end
When you set up a one or many to many relationship in rails the model gets a _ids setter:
User.find(1).store_ids = [1,2,3]
This would for example setup a relation between user 1 and the stores with ids 1,2 and 3.
The built in Rails collection form helpers make use of this:
<%= form_for(#user) do |f| %>
<% f.collection_check_boxes(:store_ids, Store.all, :id, :name) %>
<% end %>
This creates a list of checkboxes for each store - if an association exists it will already be checked. Note that we are not using fields_for since it is not a nested input.
SimpleForm has association helpers which add even more sugar.
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.association :stores, as: :check_boxes %>
<%= f.button :submit %>
<% end %>

Writing Nested Form in RoR

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 :)

Ruby on rails: Trouble creating an object with multiple associations

I am having a problem with creating an object with an association.
I have a Message model that belongs_to a job, and a user or runner. Inside my jobs/index.html I want to show a list of jobs with their corresponding messages and a form to create a new message for that particular job.
The problem is whenever I create a message, job_id stays nil. I am new to ruby on rails, so I still dont fully understand this stuff.
Here is part of my jobs/index.html (NOTE: not my actual code, I am in class so I just typed this up, may contain syntax errors).
<% #jobs.each do |job| %>
<p> <%= job.body %> </p>
<%= form_for job.messages do |f| %>
<%= f.label :body %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
<%if job.messages.present? %>
<ul>
<% job.messages.each do |message| %>
<li>
<p> message.description <p>
</li>
<% end %>
</ul>
<% else %>
<p> No messages <p>
<% end %>
<% end %>
Here is the create method in message controller (NOTE: current_login can be a runner or user, they both share the same attributes)
def create
#message = current_login.messages.new(params[:message])
#message.save
end
Job controller index action
def index
#jobs = Job.all
end
Message model
class Message < ActiveRecord::Base
attr_accessible :description
belongs_to :user
belongs_to :runner
belongs_to :job
end
User model
class User < ActiveRecord::Base
attr_accessible :username
has_many :jobs
end
Runner model
class Runner < ActiveRecord::Base
attr_accessible :username
has_many :jobs
end
Job model
class Job < ActiveRecord::Base
attr_accessible :body
has_many :messages
belongs_to :user
belongs_to :runner
end
Whenever I submit the message form inside the jobs/index.html view, it seems to successfully create a message with user_id or runner_id successfully filled out (depending on who posted the message), but I am getting nil for the job_id attribute.
Since your message belongs to job, i think you should be creating the nested resources within the jobs form.
Your new controller function inside the jobs model should build the association like so:
def new
#job = Job.new(params[:job])
#message = #job.build_message
end
your create model just needs to save the parent model:
def create
#job = Job.create(params[:job])
end
For lots of detailed information on how to do this, watch this railscast: http://railscasts.com/episodes/196-nested-model-form-part-1
I should also add, if you are simply trying to add a message to an existing job, just pass the parameter for the job_id correctly in your form, AND make sure the job you're referencing actually exists.
To solve this problem, I decided to manually create the tie between the message and the job it belongs to through a hidden field in the form.
<%= form_for(#message) do |f| %>
<%= f.label :body, "Description" %>
<%= f.text_area :body %>
<%= f.hidden_field :job_id, value: job.id %>
<%= f.submit 'Create message', class: 'button small secondary' %>
<% end %>

How to get Rails build and fields_for to create only a new record and not include existing?

I am using build, fields_for, and accepts_nested_attributes_for to create a new registration note on the same form as a new registration (has many registration notes). Great.
Problem: On the edit form for the existing registration, I want another new registration note to be created, but I don't want to see a field for each of the existing registration notes.
I have this
class Registration < ActiveRecord::Base
attr_accessible :foo, :bar, :registration_notes_attributes
has_many :registration_notes
accepts_nested_attributes_for :registration_notes
end
and this
class RegistrationsController < ApplicationController
def edit
#registration = Registration.find(params[:id])
#registration.registration_notes.build
end
end
and in the view I am doing this:
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :registration_notes do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>
and it is creating a blank text area for a new registration note (good) and each existing registration note for that registration (no thank you).
Is there a way to only create a new note for that registration and leave the existing ones alone?
EDIT: My previous answer (see below) was bugging me because it's not very nice (it still loops through all the other registration_notes needlessly). After reading the API a bit more, the best way to get the behaviour the OP wanted is to replace:
<%= r.fields_for :registration_notes do |n| %>
with:
<%= r.fields_for :registration_notes, #registration.registration_notes.build do |n| %>
fields_for optionally takes a second parameter which is the specific object to pass to the builder (see the API), which is built inline. It's probably actually better to create and pass the new note in the controller instead of in the form though (just to move the logic out of the view).
Original answer (I was so close):
Just to clarify, you want your edit form to include a new nested registration note (and ignore any other existing ones)? I haven't tested this, but you should be able to do so by replacing:
<%= r.fields_for :registration_notes do |n| %>
with:
<%= r.fields_for #registration.registration_notes.build do |n| %>
EDIT: Okay, from a quick test of my own that doesn't work, but instead you can do:
<%= r.fields_for :registration_notes do |n| %>
<%= n.text_area :content if n.object.id.nil? %>
<% end %>
This will only add the text area if the id of the registration note is nil (ie. it hasn't been saved yet).
Also, I actually tested this first and it does work ;)
If you want to create a new registration form on your edit action, you can just instantiate a new registration_note object. Right now, your form is for the existing registration object.
I believe this is what you want:
class RegistrationsController < ApplicationController
def edit
#new_registration_note = RegistrationNote.new
#registration = Registration.find(params[:id])
#registration.registration_notes.build
end
end
In your view, you should pass a hidden param that references the registration record id:
<%= form_for #new_registration_note do |r| %>
<%= r.hidden_field :registration_id, :value => #registration.id %>
<%= r.text_area :content %>
<% end %>
Now, you can create your new registration note that belongs to #registration. Make sure you have a column in your registration_notes table to point to the registration. You can read more about associations here: http://guides.rubyonrails.org/association_basics.html
Thank you so much for your help as I said in my post the only problem with the approach from "Zaid Crouch"(I don't know how to make a reference to a user hehe) is that if the form has error fields the form will be clear and boom after the page reloading you'll have nothing filled in your form and can you imagine if you form is like 20 or 30 fields that would be a terrible user experience of course
Here is my solution that works with validation models:
class Registration < ActiveRecord::Base
attr_accessible :foo, :bar, :registration_notes_attributes
has_many :registration_notes
has_one :new_registration, class_name: 'RegistrationNote'
accepts_nested_attributes_for :new_registration
end
class RegistrationsController < ApplicationController
def edit
#registration = Registration.find(params[:id])
#registration.build_new_registration
end
end
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :new_registration do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>
I'm using simple_form in my example if you want to see the same working with validations and transaction take a look at the complete post here:
http://elh.mx/ruby/using-simple_form-for-nested-attributes-models-in-a-has_many-relation-for-only-new-records/
As Heriberto Perez correctly pointed out the solution in the most upvoted answer will simply discard everything if there's a validation error on one of the fields.
My approach is similar to Heriberto's but nevertheless a bit different:
Model:
class Registration < ActiveRecord::Base
has_many :registration_notes
accepts_nested_attributes_for :registration_notes
# Because 0 is never 1 this association will never return any records.
# Above all this association don't return any existing persisted records.
has_many :new_registration_notes, -> { where('0 = 1') }
, class_name: 'RegistrationNote'
accepts_nested_attributes_for :new_registration_notes
end
Controller:
class RegistrationsController < ApplicationController
before_action :set_registration
def edit
#registration.new_registration_notes.build
end
private
def set_registration
#registration = Registration.find(params[:id])
end
def new_registration_params
params.require(:registration).permit(new_registrations_attributes: [:content])
end
end
View:
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :new_registration_notes do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>

How to use fields for a nested form outside of the nested form to act as a "global field"?

If I have the models with the following associations:
class Business
has_many :products
class Product
belongs_to :business
And I generate 3 products in the controller:
def new
#business = Business.new
3.times do
#business.products.build
end
end
Making my form look like this:
<%= form_for #business do |f| %>
<% f.text_field :business_name %>
<%= f.fields_for :products do |pf| %> # x2 more products generated
<% pf.text_field :name %>
<% pf.text_field :price %>
<% pf.text_field :date %>
<% end %>
If I want one of the fields to act as a global field for the rest of the products how could I take a field like the :price and put it outside of the f.fields_for :products to have it be the :price for all of the products?
Thank you.
If you need to initialize the price, do it in the controller. But if you need a field that doesn't map to a model directly, use the regular form helpers:
<%= text_field_tag 'global_price' %>
and then in the controller on the create action, it is available as
params[:global_price]
Alternately, you could define a method in your Business model:
def global_price=
#do something with the global price, such as updating child object...
# I'm not sure if the child form objects have been instantiated yet though
end
and then you can use it in your business form:
<%= f.text_field :global_price %>
If you need to update the child objects, you might have to do that at a later time; instead of that method, make it
attr_accessor :global_price
Which makes it an instance variable. Then you can use a before_save filter to update the child objects.
before_save :update_global_price
def update_global_price
#do something with #global_price
end

Resources