Ruby on Rails: Display relationship between 2 models - ruby-on-rails

I have an authors page that displays all the authors in the database.
<h1>Listing authors</h1>
<table>
<tr>
<th>Name</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #authors.each do |author| %>
<tr>
<td><%= author.name %></td>
<td><%= link_to 'Show', author %></td>
<td><%= link_to 'Edit', edit_author_path(author) %></td>
<td><%= link_to 'Destroy', author, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<%= link_to 'New Author', new_author_path %>
And for each author, you click show, to bring up their own page.
<p>
<b>Name:</b>
<%= #author.name %>
</p>
<%= link_to 'Edit', edit_author_path(#author) %> |
<%= link_to 'Back', authors_path %>
Now I have the same set up for books, where the user can enter new books, show and edit books in the database.
I then set up a model called authorbooks that holds a relation ship between authors and books using has_many, and belongs_to in the models for author.rb, book.rb and authorbook.rb.
I am wanting the show page of the author to display every book that they are related to.
How would I go about this? I am new to rails and still learning so please remember when answering. Thanks in advance.
EDIT model code for each model:
author.rb
class Author < ActiveRecord::Base
attr_accessible :name
validates :name, :presence => true
has_many :authorbooks
has_many :books, :through => :authorbooks
end
book.rb
class Book < ActiveRecord::Base
attr_accessible :name
validates :name, :presence => true
has_many :authorbooks
has_many :authors, :through => :authorbooks
end
authorbook.rb
class Authorbook < ActiveRecord::Base
attr_accessible :author_id, :book_id
belongs_to :book
belongs_to :author
end

It would have been interesting to see the model code as well. I assume you have something like:
class Author
has_many :author_books
has_many :books, :through => :author_books # this line might be missing,
# read in the api documentation about it.
class AuthorBooks
belongs_to :author
belongs_to :book
Now you can do something like:
<h3>Related books</h3>
<ul>
<% #author.books.each do |book| %>
<li><%= book.name %> <%= link_to "Details", book_path(book) %></li>
<% end %>
</ul>
Without the :through line you could have done something like:
#author.author_books.each do |ab|
... ab.book.name ...
Note 1: you get N+1 load problems with the second example. See the eager loading chapter in A::R guide for more information about that.
Note 2: Checkout HAML; much nicer than ERB

Related

rails multi-level nested forms with cocoon and tables

I've successfully implemented one level of nested form with Cocoon and tables. However, I'm having a difficult time wrapping my mind on how to do another nested level. My issue is how to do this with a table. And maybe a table isn't the write way to go at all. Thank you for helping a newbie.
Here are my models:
class Profession < ApplicationRecord
has_many :procedure_categories, dependent: :destroy
accepts_nested_attributes_for :procedure_categories, allow_destroy: true
end
And:
class ProcedureCategory < ApplicationRecord
belongs_to :profession
has_many :procedures
accepts_nested_attributes_for :procedures, allow_destroy: true
end
And:
class Procedure < ApplicationRecord
belongs_to :procedure_category
end
Here is my top level form code:
<%= form_for(#profession) do |f| %>
<%= render 'shared/profession_error_messages' %>
<%= f.label :profession %>
<%= f.text_field :profession, class: 'form-control' %>
<%= f.label :description %>
<%= f.text_field :description, class: 'form-control' %>
<%= f.label :active, class: "checkbox inline" do %>
<%= f.check_box :active %>
<span>Active profession?</span>
<% end %>
<table class='table'>
<thead>
<tr>
<th>Category</th>
<th>Description</th>
<th>Display Order</th>
<th>Selection Type</th>
<th>Delete</th>
<th>Edit</th>
</tr>
</thead>
<tbody class="categories">
<%= f.fields_for :procedure_categories do |procedure_category| %>
<%= render 'procedure_category_fields', f: procedure_category %>
<% end %>
</tbody>
</table>
<%= link_to_add_association 'Add Category', f, :procedure_categories,
data: { association_insertion_node: '.categories', association_insertion_method: :append } %>
<br><br>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
And the next partial one level down:
<tr class="nested-fields">
<td><%= f.text_field :category, class: 'form-control' %></td>
<td><%= f.text_field :description, class: 'form-control' %></td>
<td><%= f.text_field :display_order, class: 'form-control' %></td>
<% cs = options_for_select(controls, f.object.selection_type) %>
<td><%= f.select :selection_type, cs, class: 'form-control' %></td>
<td><%= link_to_remove_association "Remove Category", f %></td>
<% if f.object != nil %>
<td><%= link_to "Category", edit_procedure_category_path(#profession,f.object) %><td></td>
<% end %>
</tr>
So, I'm struggling with how to implement the final level of nesting (procedures).
Thank you for listening.
Use has_many :through
Here are my models:
class Profession < ApplicationRecord
has_many :procedures, through: categories
has_many :categories, dependent: :destroy
accepts_nested_attributes_for :categories, allow_destroy: true
end
Rename this procedure_category model in category
class Category < ApplicationRecord
belongs_to :profession
has_many :procedures
accepts_nested_attributes_for :procedures, allow_destroy: true
end
And:
class Procedure < ApplicationRecord
belongs_to :category
end
If I miss something you can check the instruction from the rails guide
The controller professions#new action should create the following variables, so that they are available in the view:
def new
#profession = Profession.new
#categories = #profession.categories.build
#procedures = #categories.procedures.build
end
The view uses that variable so store the user inputs and make a post request at /profession/ with those inputs stored in the parameters hash
<%= form_for(#profession) do |f| %>
<%= f.fields_for :categories do |category| %>
<%= category.fields_for :procedures do |precedure| %>
<% end %>
<% end %>
<% end %>
The fields_for yields a form builder. The parameters' name will be what accepts_nested_attributes_for expects. For example, when creating a user with 2 addresses, the submitted parameters would look like:
This is how your parameters should look like:
{
'profession' => {
'name' => 'John Doe',
'categories_attributes' => {
'0' => {
'kind' => 'Home',
'street' => '221b Baker Street',
'procedures_attributes' => {
'0' => {},
'1' => {}
}
},
'1' => {
'kind' => 'Office',
'street' => '31 Spooner Street'
}
}
}
}
so make sure your form is pointing at post url /professions/ and that the routing will trigger the professions#create action
def create
binding.pry
end
for any problems set a binding pry and check in your console how your parameters are showing up.
Read more at
http://guides.rubyonrails.org/form_helpers.html#building-complex-forms

How to display model fields from another model using nested resource rails 5

I have 3 models, first product model, invoice and invoice_detail.
Invoice(Parent) and invoice detail(Child) are nested resource.
Am adding products from invoice detail model, that's working fine, the problem is when am trying to display in a table the name of the product that was added, am getting the follow error:
Thanks in advance!!
invoices/show.html.erb
undefined method `name' for 2:Integer
invoices/show.html.erb
<% #invoice.invoice_details.each do |invoice_detail| %>
<tr>
<td><%= invoice_detail.product_id.name %></td>
<td><%= number_to_currency invoice_detail.product_id.price %></td>
<td><%= invoice_detail.product_id.quantity %></td>
<td><%= number_to_currency invoice_detail.total_amt %></td>
<td>
<%= link_to "Destroy",
[#invoice, invoice_detail],
method: :delete,
remote: true,
data: { confirm: "Are you sure?" },
class: "btn btn-default"%>
</td>
</tr>
<% end %>
models/product.erb
class Product < ApplicationRecord
belongs_to :category
belongs_to :brand
belongs_to :unit
belongs_to :warehouse
belongs_to :user
has_many :invoice_details
end
models/invoice.erb
class Invoice < ApplicationRecord
belongs_to :warehouse
belongs_to :invoice_type
belongs_to :user
belongs_to :customer
belongs_to :provider
has_many :invoice_details, :dependent => :destroy
end
models/invoice_detail.erb
class InvoiceDetail < ApplicationRecord
belongs_to :invoice
belongs_to :product
belongs_to :tax
end
In your view,
<%= invoice_detail.product_id.name %>
should be
<%= invoice_detail.product.name %>

Why is fields_for not iterating over the passed ActiveRecord::Relation?

I'm trying to setup a section of the form where a user can select zero or more doctors, but is required to provide a token from each to demonstrate that they spoke to the doctor. Anyway, I have the relationship working on the models, and can set & get lists of doctors and patients. But fields_for doesn't seem to understand that. There's probably some magic somewhere that I've missed, and if anyone can point it out that'd be great.
in the controller:
def edit
#doctors = User.doctor
super
end
in the model:
class User < ActiveRecord::Base
devise :registerable
has_secure_token
enum role: [:doctor, :admin, :customer, :distributor]
enum salutation: [:mr, :mrs, :ms, :miss, :dr]
has_and_belongs_to_many :doctors, -> { doctor }, {class_name: "User", join_table: :doctors_patients, foreign_key: :patient_id, association_foreign_key: :doctor_id}
has_and_belongs_to_many :patients, {class_name: "User", join_table: :doctors_patients, foreign_key: :doctor_id, association_foreign_key: :patient_id}
end
in the view:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put,class:"form-horizontal" }) do |f|
...
<%= f.fields_for :doctors, #doctors do |d| %>
<tr>
<td><%= d.check_box :id,{checked: resource.doctors.include? d.object.id} %></td>
<td><%= "#{d.object.salutation.titleize}. #{d.object.last_name}" %></td>
<td><%= "#{d.object.clinic}" %></td>
<td><%= d.text_field :token %></td>
<td><%= link_to 'More','#',class:"btn btn-primary",data:{toggle:"modal",target:"#doctor#{d.object.id}Modal"} %></td>
</tr>
<% end %>
<% end %>
I'm currently getting an error: undefined method 'id' for #<User::ActiveRecord_Relation:0x007faa9392c9b8> because the entire relation is being used in fields_for not one instance at a time.
I'm just trying to implement this:
Or a collection to be used:
<%= form_for #person do |person_form| %>
...
<%= person_form.fields_for :projects, #active_projects do |project_fields| %>
Name: <%= project_fields.text_field :name %>
<% end %>
...
<% end %>
from: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Turns out you need accepts_nested_attributes_for :doctors in your model if you want to get the iteration from fields_for.
replace line <%= f.fields_for :doctors, #doctors do |d| %> with <%= f.fields_for :doctor do |d| %>.

Rails model relationship doubts

I have these models
class Course < ActiveRecord::Base
attr_accessible :name
has_many :teachers
end
class Teacher < ActiveRecord::Base
attr_accessible :id, :name, :course_id
belongs_to :course
has_many :evaluations
end
class Evaluation < ActiveRecord::Base
attr_accessible :teacher_id, :course_id
belongs_to :teacher
end
this is the views/evaluations/index.html.erb file
<% #evaluations.each do |evaluation| %>
<tr>
<td><%= evaluation.teacher_id %></td>
<td><%= link_to 'Show', evaluation %></td>
<td><%= link_to 'Edit', edit_evaluation_path(evaluation) %></td>
<td><%= link_to 'Destroy', evaluation, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
</tr>
<% end %>
I want to display the teacher's name with:
<td><%= evaluation.teacher.name %></td>
but it doesn't work.Rails shows this error:
"undefined method `name' for nil:NilClass"
Can anyone help me?
For all of your evaluation rails will get the teacher and then display it's name. If only one evaluation has no teacher, it will get nul for the teacher and then try to get name on that nil, then you have your error.
Try this :
<td><% if evaluation.teacher %>
<%= evaluation.teacher.name %>
<% end %></td>

rails how to do f.parent.text_field :name

I need to put fields from journals and journal_entries into one row on a table, and have the ability to add and show many data entry rows in the same view. (ie a table of rows and using link_to_add_fields with accepts_nested_attributes to expand the rows in the table).
There has to be some kind of f.parent.text_field or f.object.parent.text_field?
I'm trying to do something like the following
<table>
#in a :pm namespace
<%= form_for [:pm, #lease] do |f| %>
<%= f.fields_for :journal_entries do |journal_entries| %>
<%= render "journal_entry_fields" , f: journal_entries %>
<% end %>
<%= link_to_add_fields "+ Add transactions", f, :journal_entries %>
<% end %>
</table>
_journal_entry_fields.html.erb
<fieldset>
<tr>
## HERE IS WHAT I'M LOOKING FOR <<<<<<<<<<<!!>>>>>>>>>>>>>
<td><%= f.parent.text_field :dated %></td>
<td><%= f.parent.text_field :account_name %></td>
<td><%= f.text_field :credit %></td>
<td><%= f.text_field :notes %></td>
</tr>
</fieldset>
My Models
class Lease < ActiveRecord::Base
has_many :journals, :order => [:dated, :id] #, :conditions => "journals.lease_id = id"
has_many :journal_entries, :through => :journals
accepts_nested_attributes_for :journal_entries , :allow_destroy => true
accepts_nested_attributes_for :journals , :allow_destroy => true
end
class Journal < ActiveRecord::Base
belongs_to :lease, :conditions => :lease_id != nil
has_many :journal_entries
accepts_nested_attributes_for :journal_entries , :allow_destroy => true
end
class JournalEntry < ActiveRecord::Base
belongs_to :journal
end
I'm using Rails 3.2.12 and ruby 1.9.3
I'm trying to see if this is a better solution than the problem faced on: rails link_to_add_fields not adding fields with has_many :through (with nested form inside)
I made a different thread because I think it's so drastically different.
Thanks,
Phil
As per my understanding about your use-case you want to create journals and its entries in a single form of Lease. So, you can us the fields_for for both of them as below:
<table>
#in a :pm namespace
<%= form_for [:pm, #lease] do |f| %>
<%= f.fields_for :journals do |journal| %>
<%= render "journal_entry_fields" , f: journal %>
<% end %>
<%= link_to_add_fields "+ Add transactions", f, :journals %>
<% end %>
</table>
_journal_entry_fields.html.erb
<fieldset>
<tr>
<td><%= f.text_field :dated %></td>
<td><%= f.text_field :account_name %></td>
<%= f.fields_for :journal_entries do |journal_entry| %>
<td><%= journal_entry.text_field :credit %></td>
<td><%= journal_entry.text_field :notes %></td>
<% end %>
</tr>
</fieldset>
Though you need to initialise the journal entries every time a new record is added dynamically. I can't help you with this right now as I'm not on my PC.
Try this: http://railscasts.com/episodes/196-nested-model-form-revised
The RailsCasts model relations are similar to your model relations, although you would need to alter the HTML.
RailsCasts Models: Survey > Question > Answer
Your Models: Lease > Journal > JournalEntry

Resources