Accessing model has_many property - ruby-on-rails

I have the following models:
Post.rb
has_many :likes
belongs_to :user
Like.rb
belongs_to :user
belongs_to :post
User.rb
has_many :posts
has_many :likes
I'm trying to query the user likes in the view like this:
<% #user.likes.each do |i| %>
<%= i.post.title %>
<% end %>
and it returns undefined method 'title' for nil:NilClass
I only can reach the array of posts like this:
<% #user.likes.each do |i| %>
<%= i.post %>
<% end %>
Or like this <%= #user.likes.first.post.title %>
I can't understand why I can't reach the "title" or any other property.
Any help?

It seems at least one of #user.likes doesn't have its associated post. So Like#post returns nil, and on that nil object you try to call title which raises an error. If this situation is not desired, you probably have to validate likes so that every Like has its post:
class Like < ActiveRecord::Base
# ...
validate :post, presence: true
if so, you probably should also make sure associated likes are destroyed when the post is destroyed:
class Post < ActiveRecord::Base
# ...
has_many :likes, dependent: :destroy
You also can fetch likes only with associated post like this:
<% #user.likes.joins(:post).each do |i| %>
or use try in view, so that when like without post is reached, no error is raised:
<%= i.post.try(:title) %>

If your like always belongs to post, it would be better to have user association with likes using "through: :posts" option. That is correct way of association in this scenario.
So your code will be as follows:
Post.rb
has_many :likes
belongs_to :user
Like.rb
belongs_to :post
User.rb
has_many :posts
has_many :likes, through: :posts
and then you can access user's likes as:
<% #user.likes.each do |i| %>
<%= i.post.title %>
<% end %>
Which will always contain post title.

Related

Rails 4 one-to-many relationship: find parent attribute from child parent_id attribute

I have five models:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
has_many :comments
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
has_many :posts
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
The comment table has the following columns: id, post_id, user_id and body.
In different views, for instance in the show.html.erb post view, I need to display the relevant comments with the first name of the user who posted the comment.
In other words, I am trying to retrieve user.first_name from comment.user_id.
To achieve this, I defined the following method in the comment.rb file:
def self.user_first_name
User.find(id: '#{comment.user_id}').first_name
end
and then I updated the show.html.erb post view as follows:
<h3>Comments</h3>
<% #post.comments.each do |comment| %>
<p>
<strong><%= comment.user_first_name %></strong>
<%= comment.body %>
</p>
<% end %>
When I do that, I get the following error:
NoMethodError in Posts#show
undefined method `user_first_name' for #<Comment:0x007fc510b67380>
<% #post.comments.each do |comment| %>
<p>
<strong><%= comment.user_first_name %></strong>
<%= comment.body %>
</p>
<% end %>
I don't really understand Why I get an error related to Posts#show.
Any idea how to fix this?
Replace:
comment.rb
def self.user_first_name
User.find(id: '#{comment.user_id}').first_name
end
with:
comment.rb
delegate :first_name, to: :user, prefix: true
If you do this, you can just make the same call comment.user_first_name and it will give you the user's first name. Add , allow_nil: true if you don't want it to break if the user doesn't have a first_name.
You also might want to add:
has_many :comments
user.rb
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
has_many :comments
end

Rails Join Table Nested form_for

I'm learning Rails building an ordering system and I'm stuck trying to build a form for Orders. Where Orders is a nested resource for Restaurant.
Routes:
resources :restaurants do
resources :orders
resources :recipes
end
My models look like this:
class Restaurant < ActiveRecord::Base
has_many :orders
has_many :recipes, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes, dependent: :destroy
has_many :recipes, through: :order_recipes
end
class Recipe < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes
has_many :orders, through: :order_recipes
end
My controller:
#recipes = #restaurant.recipes
#order_recipes = #recipes.map{|r| #order.order_recipes.build(recipe: r)}
And my view:
<%= form_for([#restaurant, #order]) do |order_form| %>
<%= order_form.label :Table_Number %>
<%= order_form.number_field :table_id %>
<%= order_form.fields_for :order_recipes, #order_recipes do |orf| %>
<%= order_form.hidden_field :recipe_ids %>
<%= order_form.label Recipe.where(id: :recipe_id) %>
<%= orf.number_field :quantity %>
My current problem is displaying the names of each recipe. It seems that :recipe_id is being passed as null all the time. My ultimate goal is to be able to build order_recipes populating the quantity column, and I thought having the recipe_id from order_recipes I could also access the correct recipe object from the DB to display the name or any other relevant data.
try so in your controller:
#order_recipes = #recipes.map{|r| #order.build_order_recipes(recipe: r)}
Eventually I got this the answer posted in this question to work: Rails 4 Accessing Join Table Attributes
I'm only struggling with passing the correct parameters now, but the form is correct.
I assumed, you have a restaurant, it has recipes, it takes orders, orders are tracked by order_recipes table.
# controller
#recipes = #restaurant.recipes
#order = #recipes.map{|r| r.order.build}
# view
<%= form_for([#restaurant, #order]) do |order_form| %>
...
<% #order.each do |index, ord| %>
<%= order_form.fields_for :orders, #order do |orf| %>
...
<%= order_form.label #recipes[index] %>
<%= orf.number_field :quantity %>
# also in restaurant model
accepts_nested_attributes_for :orders

Fetching data from has_one model

I have this models:
class User < ActiveRecord::Base
has_one :page, dependent: :destroy
end
class Page < ActiveRecord::Base
attr_accessible :infos, :title, :user_id
belongs_to :user
end
in the page index view, when I do
<%= page.user.name %>
I can get the value, but in the user page view, when I do
<%= user.page %>
I get the object: #<Page:0x000000045a0470>
and when I do <%= user.page.title %> I get the error: undefined method `title' for nil:NilClass
how can I do to get the values from has_many model in the right way?
thanks!
I figure it out:
Not all users has a page, so I do this:
change
<%= user.page.title %>
to
<%= user.page.try(:title) %>
Then it fix the error. =D

fields_for and has_many_through

I have these models:
class Order < ActiveRecord::Base
has_many :order_lines
has_many :prizes, :through => :order_lines
accepts_nested_attributes_for :order_lines
end
class Prize < ActiveRecord::Base
has_many :order_lines
end
class OrderLine < ActiveRecord::Base
belongs_to :order
belongs_to :prize
end
I would like a nested form in the order form that displays every prize with a text box next to it where the user can enter pieces (eg. the amount to order). When the form is submitted the create action should create the order_lines accordingly. I can't find a solution anywhere.
First of all in the Order model use accepts_nested_attributes_for :prizes, instead of what you have.
After that it's easy, just add a form in the view (there's no additional step controller)
<%= form_for #order do |order_form| %>
..
<%= order_form.fields_for :prizes do |prizes_form| %>
<%= prizes_form.text_field :piece %>
..
<% end %>
..
<% end %>
This is straight from the documentation... You should definitely check that first.

Rails 3 Data Modeling Help - Has Many, Belongs to, Nested Atrributes

I am working on a project involving three models (recipient, award, announcer) and need to have a nested attributes when issuing an award by an announcer to multiple recipients. For an example, award form need to have the ability to do 3 things:
Can add multiple-recipients (i.e. "add recipient", "remove recipient") - nested attributes
After creating a new award, the award will be posted into recipient's profile.
Enables future polling of #recipient.awards and #announcer.awards
Really struggle in terms of how to smartly solve this problem. The following data structure kind of made sense, however can not do "accepts_nested_attributes_for :recipients" in the award form. Can you help? Many thanks in advance.
class Recipient < ActiveRecord::Base
has_many :awards
has_many :announcers, :through => :awards
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
class Award < ActiveRecord::Base
belongs_to :announcer
belongs_to :recipient
end
You're just about there. The main issue is that you're trying to create recipient objects in the form rather than just creating a relationship between the award and another object (user). You could do something like this:
class User < ActiveRecord::Base
has_many :recipients
has_many :awards, :through => :recipients
end
# this is your relationship between an award and a user
class Recipient < ActiveRecord::Base
belongs_to :user
belongs_to :award
end
class Award < ActiveRecord::Base
has_many :recipients
has_many :users, :through => :recipients
belongs_to :announcer
accepts_nested_attributes_for :recipients, :allow_destroy => true
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
Then you would just do a nested form that would build the recipients_attributes array:
<%= form_for #award do |f| %>
<%= f.text_field :name %>
<div id="recipients">
<% #award.recipients.each do |recipient| %>
<%= render :partial => '/recipients/new', :locals => {:recipient => recipient, :f => f} %>
<% end %>
</div>
<%= link_to_function 'add recipient', "jQuery('#recipients').append(#{render(:partial => '/recipients/new').to_json})" %>
<% end %>
And, to keep it DRY just push the nested part into a partial:
# app/views/recipients/_new.html.erb
<% recipient ||= Recipient.new %>
<%= f.fields_for 'recipients_attributes[]', recipient do |rf| %>
<%= rf.select :user_id, User.all %>
<%= fr.check_box '_delete' %>
<%= fr.label '_delete', 'remove' %>
<% end %>
Obviously the User.all call isn't ideal so maybe make that an autocomplete.

Resources