I have 3 models as below.
Model for Item
class Item < ApplicationRecord
belongs_to :item_type, :class_name=>ItemType, :foreign_key=>"item_type_id"
end
and Model for Ingredients
class Ingredient < ApplicationRecord
validates_presence_of :ingredient, :message=>"name cannot be blank!"
end
and model for recipe_ingredients
class RecipeIngredient < ApplicationRecord
belongs_to :item, :class_name=>Item, :foreign_key=>"item_id"
belongs_to :ingredient, :class_name=>Ingredient, :foreign_key=>"ingredient_id"
validates_numericality_of :quantity
end
The table for ingredients has following columns
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"ingredient" varchar,
"inventory_unit" varchar,
"recipe_unit" varchar,
"created_at" datetime NOT NULL,
"updated_at" datetime NOT NULL
The view for index of recipe ingredients is as below. The ingredients are appearing in the view. I want to pull recipe unit from ingredients table for the selected ingredient and display it in the table. I have tried it doing a query. The code is as below.
<p id="notice"><%= notice %></p>
<h1>Recipe Ingredients</h1>
<table>
<thead>
<tr>
<th>Item</th>
<th>Ingredient</th>
<th>Quantity</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #recipe_ingredients.each do |recipe_ingredient| %>
<tr>
<td><%= recipe_ingredient.item %></td>
<td><%= recipe_ingredient.ingredient.ingredient %></td>
<td><%= recipe_ingredient.quantity %></td>
<td><%= Ingredient.select('recipe_unit').where(:id => #ingredient_id) %></td>
<td><%= link_to 'Show', recipe_ingredient %></td>
<td><%= link_to 'Edit', edit_recipe_ingredient_path(recipe_ingredient) %></td>
<td><%= link_to 'Destroy', recipe_ingredient, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Recipe Ingredient', new_recipe_ingredient_path %>
But the output I am getting is as shown in image given below.
You can simply type the following:
recipe_ingredient.ingredient.recipe_unit
Why is this possible?
Since a RecipeIngredient belongs_to an Ingredient, you can access the Ingredient of a RecipeIngredient as if it was any other field like this:
recipe_ingredient.ingredient
Once you have the ingredient, you can pull the recipe_unit directly by typing:
recipe_ingredient.ingredient.recipe_unit
Side note:
You can omit the :class_name and :foreign_key hashes from your associativity rules in your model classes. For example, take this class:
class Student < ActiveRecord::Base
belongs_to Course
end
By convention, rails will associate the Student class with the Course class. Rails will look for a column in the "students" database table labeled "course_id", and if "course_id" exists as a column in the table, Rails will automatically use that field as the foreign key for Student. You can use the :foreign_key => 'some_key' notation when you want to override the default conventions in rails (which isn't recommended unless its strictly necessary).
For more reading on the subject of active record migrations and foreign key associations, you can check out the rails docs.
Ok, it's displaying as it is because you are displaying the whole class, not an actual detail of the instance.
In this case below, you are selecting a whole set of Ingredients (the set contains only one ingredient, but it will always return a set):
Ingredient.select('recipe_unit').where(:id => #ingredient_id)
What you probably want is something like this:
Ingredient.find(#ingredient_id).recipe_unit
The differences:
where always returns a set of items (even if there's just one item in the set) What is returned is not an ingredient, but an Active Relation. To just return one ingredient, you need to use find or first (or a similar such method).
select just tells Rails what to pull out of the db... it doesn't tell the template what to display in the template. It says "only fetch the recipe_unit column from the db" it doesn't say and then output therecipe_unitcolumn - for that you need to actually call the recipe_unit method on the ingredient (as in the example I gave).
Note that you have the same problem with recipe_ingredient.item
What column from an item do you want to display? does it have a name or a description if so - you need to actually call that eg:
<%= recipe_ingredient.item.name %>
First of all, you may omit :class_name and :foreign_key from your models because of convention over configuration - your models are named exactly like classes and foreign keys you list.
As for your question, you need to use yak shaving for that:
recipe_ingredient.ingredient.recipe_unit
Related
I'm attempting to return the specific hospital name associated to a patient but keep getting errors.
Models:
Hospital
has_many :patients
Patients
belong_to :hospital
When rendering my page I call the controller:
def list_patients
#patients = Patient.all
end
In my view I print out each patient and their information:
<% #patients.each do |patient| %>
<table>
<tr>
<td><%= patient.first_name + "," + patient.last_name %></td>
<td><%= patient.ssn %></td>
<td><%= patient.dob %></td>
<td><%= patient.hospital.name%></td>
</tr>
</table>
The above returns an "undefined method for name". If I remove name I can see that a object (<Hospital:0x007fa1d9530138>)
is returned, but I'm unable to then access the specific attributes within the object.
I can return the specific hospital ID, if I do something like:
patient.hospital_id
but am then stuck on how to get to the hospital name.
Is your code equal to the pasted one?
If so, is belongs_to and not belong_to
If you also can't do Hospital.first.patients in the console, make sure you have a hospital_id in you patient model
I am still fairly new to Rails and fairly sure the way I'm attempting to go about this is inefficient or just plain silly, but here's what I'm trying to accomplish. I have 2 models, Cases (patient case files) and Inventories (medical transplant materials used in Cases).
class Case < ActiveRecord::Base
has_many :inventories
accepts_nested_attributes_for :inventories, :reject_if => :all_blank
end
class Inventory < ActiveRecord::Base
belongs_to :case
end
Inventories are created through a separate process and the goal is to associate them with a Case through the Case form. What I am trying to do is put a table on my Case form that lists the available Inventories along with checkboxes to select the desired Inventories to associate with the Case being created. This is further complicated by the fact that I need to be able to include nested fields for a couple of attributes on each Inventory (:case_price and :case_ship_price). I had previously done this in a very roundabout way using a has_many through association and storing those attributes on the pivot table, but it involved some hacky code to capture the field inputs from params and then save them through this block:
class CasesController < ApplicationController
def create
#case = Case.new(params[:case])
if #case.save
#case.case_lineitems.each do |li|
li.update_attributes(:price => params[:lineitem_price][li.inventory_id.to_s],
:shipping_cost => params[:lineitem_shipping][li.inventory_id.to_s])
end
redirect_to #case
else
render 'new'
end
end
end
This felt extremely clumsy and I was worried about problems it might cause, so I wanted to give a simple has_many, belongs_to relationship a try. However, I'm not sure if the typical <%= check_box_tag :inventory_ids, inventory.id, #case.inventories.include?(inventory), name: 'case[inventory_ids][]' %> works for that type of relationship. Here is what this section of my form looks like presently:
<table>
<thead>
<tr>
<th></th>
<th>Product</th>
<th>Serial #</th>
<th>Price</th>
<th>Shipping</th>
</tr>
</thead>
<tbody>
<% #inventories.each do |inventory| %>
<tr>
<td>
<%= check_box_tag :inventory_ids, inventory.id, #case.inventories.include?(inventory), name: 'case[inventory_ids][]' %>
</td>
<td><%= inventory.product.name %></td>
<td><%= inventory.serial_num %></td>
<%= f.fields_for :inventories, inventory do |inv| %>
<td>
<%= inv.text_field :case_price %>
</td>
<td>
<%= inv.text_field :case_ship_price %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
This results in the first checkbox defaulting to checked, and if I leave all unchecked, all of the inventories become associated upon submission. Checking only a subset results in an exception along the lines of Couldn't find Inventory with ID=206 for Case with ID=. Finally, checking all of the Inventories seems to result in the associations and nested attributes saving correctly.
How do I clean this up so that it works as desired? And if I need to go back to a has_many through relationship, is there a better way to save attributes on the pivot table on the same form that creates the row on pivot table? I'd really appreciate any help with this, as no amount of searching has gotten me out of this challenge.
<% #blog.blog_comment_types.each do |blog_comment_type| %>
<tr>
<td><%= blog_comment_type.comment_type_id %></td>
<td>Comment name goes here</td>
</tr>
<% end %>
I want to be able to output the comment name based off the comment_type_id.
I an looping through the blog_comment_type table, I want to use the "comment_type_id" column so I can pull from the following table: comment_type which has the name field I want to output. The comment_type table has an id which is the referenced comment_type_id being looped through.
Is there a best practice in Rails to do so within a view?
Tbl: comment_type
fields:
id
name
tbl: blog_comment_type
fields:
id
comment_type_id (this is the matching id in the comment_type table).
Thanks!
In Rails and beyond, the best practice is not to do this in a view. Instead, if you setup your Blog object so it knows about the comment types associated with it, then the problem becomes pretty simple:
class Blog
has_many :blog_comment_types
....
end
then in your view:
<% #blog.blog_comment_types.each do |blog_comment_type| %>
<tr>
<td><%= blog_comment_type.id %></td>
<td><%= blog_comment_type.name %></td>
</tr>
<% end %>
I have tried a couple other similar posts but am still getting an error.
In the Posts model I have a category_id field. I have the following models:
#Posts model
belongs_to :categories
#Category model
has_many :posts
In the Posts index controller I have:
#categories = #posts.Category.find(:all, :order => 'categoryname')
In the View I have:
<% #posts.each do |post| %>
<tr>
<td><%= post.category_id %></td>
<td><%= #categories.categoryname %></td>
<td><%= link_to 'View', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
</tr>
<% end %>
In the 2nd column I am trying to show the category name ("categoryname") from the Category table instead of the category_id from the posts table. I am getting an error:
undefined method `Category' for #ActiveRecord::Relation:0x3e1a9b0>
I have also tried:
<td><%= post.categories.categoryname %></td>
But get the same error.
As well as:
<td><%= post.category.categoryname %></td>
Any suggestions would be greatly appreciated.
In your model
belongs_to :category
In your view
<td><%= post.category.categoryname %></td>
You can get rid of the #categories = line in your controller
Also, categoryname is probably not the best attribute name for your Category model. Why not just name it name. post.category.name seems a lot better than post.category.categoryname, don't you think?
Okay, a couple things
belongs_to :categories
belongs_to is a singular relationship. You should be putting
belongs_to :category
In this case you need category_id in the posts table. You would get the category by
#post.category.categoryname
Unless a post can have many categories, in which case you'd want
#Post
has_and_belongs_to_many :categories
#Category
has_and_belongs_to_many :posts
In this case you need a join table called categories_posts with two fields category_id and post_id and you would get it by calling
#post.categories.each do |cat|
cat.categoryname
end
There are some other problems with you code, like
#categories = #posts.Category.find(:all, :order => 'categoryname')
Category is a model, not your named relationship, which is probably why you are getting the exception in your application.
You will get the following error
undefined method `categoryname' for nil:NilClass on the line: <%= post.category.categoryname %>
if there are records in your table without a category specified.
In other words, make sure all of your records have a category associated with them
i Noticed that #category in your controller starts with a small letter c, but in your view, it starts with a Capital letter !
In your case, if there is a chance that you can have an empty category_id in posts table (as you mentioned in your comment), you can add
if post.category_id?
so your cell will look like this:
<td><%= post.category.categoryname if post.category_id? %></td>
If post.category_id is null it will just show an empty cell.
I have the following code to display any comments that a user has made in a table on the users show page. The code works fine and a table is diplayed with all of the users comments and the permits they belong to. Currently the table displayed shows the permits in the order they were created by the user. I would like to arrange the table so that the permit column is displayed in alphabettical order instead of when they were created. Im not sure if I need to adjust this code or something in the controller.
<% if #user.comments.exists? then %>
<b>Comments:</b><br />
<table>
<tr>
<th>Permit</th>
<th>Comment</th>
</tr>
<% #user.comments.each do |comment| %>
<tr>
<td><%= link_to comment.permit.name, comment.permit %></td>
<td><%= comment.body %></td>
</tr>
<% end %>
</table>
<% end %>
Use the ActiveRecord order method.
Check the official documentation here: http://guides.rubyonrails.org/active_record_querying.html#ordering
In your case, that would be something like:
<% #user.comments.order('name').each do |comment| %>
> sorted_permits = #user.permits.sort
This gives you a list of permits, ordered naturally (i.e. if they are String values, they are sorted alphabetically, if they are numeric values, they are sorted from lowest to highest). You can do that in your view, or more typically, in your controller.
So, if I have a list of permits such as:
permits = ["Fishing", "Hunting", "Reading", "Driving"]
Then when I can do the following:
> permits.sort
=> ["Driving", "Fishing", "Hunting", "Reading"]
NOTE that .sort returns a copy of the list, in sorted order, while the similar .sort! modifies the original list, reordering it permanently (in memory), but does not change the order/IDs of the permits in the database.
See: http://www.ruby-doc.org/core/classes/Array.html#M000244
Using the :order option directly on the relation definition should do the trick:
class Comment < ActiveRecord::Base
has_many :permits, :order => 'name DESC'
end