Need data from a many:many join in a Rails view - ruby-on-rails

Its maybe not the best solution in most cases, but i want a table with data form 3 tables.
class Media < ActiveRecord::Base
belongs_to :user
belongs_to :type
has_many :ratings
end
class User < ActiveRecord::Base
has_many :medias
has_many :ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :media
end
Thats the view I want
<table>
<tr>
<th>Name</th>
<th>Comment</th>
<th>Creator</th>
<th>Type</th>
<% for user in #users %>
<th><%=h user.login %></th>
<% end %>
</tr>
<% for media in #medias %>
<tr>
<td><%=h media.name %></td>
<td><%=h media.comment %></td>
<td><%=h media.user.login %></td>
<td><%=h media.type.name %></td>
<% for user in #users %>
<td><%=h GET_RATING (media, user) %></td>
<% end %>%>
</tr>
<% end %>
</table>
Basicly i want one new row for each users ratings for each media
What I want is a Table that looks like that:
media.name media.comment ... rating(media, user).rating
I think it would be better to use a join in the Controller with the Media find methods but I dont know how exactly, enougher possible solution could be helper method that takes media and user as parameters.
What do you think is the best solution for this?

This kind of association belongs in your model, a has many through relationship is perfect for this.
class User < ActiveRecord::Base
has_many :ratings
has_many :media, :through => :ratings
end
class Media < ActiveRecord::Base
has_many :ratings
has_many :users, :through => ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :media
end
Then you can access
media.name media.comment
Then could also access
user.ratings
or:
<% media.users.each do |user| %>
## Do stuff with user.ratings array
<% end %>
You can also:
media.ratings.each do |rating|
rating.your_attribute
rating.user.your_attribute
end

Related

Rails 5 many to many index

I am having a problem in the eyes when presenting the data.
I have 3 user models, sponsors and pets Result that sponsors in a table join between users and pets that are nan, my problem is in the hour to show all the sponsors of the mascot in sight and achievement but in a wrong way to La time to msotrar the data. I hope you can tell me how to fix it. Thank you.
Index.html.erb
<h1>Sponsors#index</h1>
<table class="table table-bordered table-hover table-striped" id="histories">
<thead>
<tr>
<th>Mascota</th>
<th>Padrinos</th>
<th>Apadrinar</th>
</tr>
</thead>
<tbody>
<% #pets.each do |pet| %>
<tr>
<td><%= pet.name %></td>
<td>
<% #users.each do |user| %>
<% #sponsors.each do |sponsor| %>
<% if user.id == sponsor.user_id and pet.id == sponsor.pet_id %>
<%= user.email%>
<% else %>
<p>No Tengo Padinos =-( </p>
<% end %>
<% end %>
<% end %>
</td>
<td><button>Apadrinar</button></td>
</tr>
<% end %>
</tbody>
</table>
My controller has the three models that I am sending to view.
def index
#sponsors = Sponsor.all
#users = User.all
#pets = Pet.all
end
pet.rb
class Pet < ApplicationRecord
has_many :adoptions
belongs_to :race, required: false
has_many :users, through: :sponsors
end
user.rb
class User < ApplicationRecord
has_many :pets, through: :sponsors
end
sponsor.rb
class Sponsor < ApplicationRecord
belongs_to :user
belongs_to :pet
end
The output that now shows me the attachment in the image.enter image description here
I also wonder if there is a better way to make the query, to show the respective data.
Another thing is how would I do to no longer be able to sponsor pets that I have already given a sponsor?
You are using a through table to join users and pets, but your associations aren't setup properly. For a through model, you have to have_many of the through table along with a has_many: :through association. Your associations should look like this:
class Pet < ApplicationRecord
has_many :adoptions
belongs_to :race, required: false
**has_many :sponsors**
has_many :users, through: :sponsors
end
class User < ApplicationRecord
**has_many :sponsors**
has_many :pets, through: :sponsors
end
Afther working and following diferent recomendations here is my solution.
<% #pets.each do |pet| %>
<tr>
<td> <%= pet.name %></td>
<td>
<% if pet.sponsors.any? %>
<% pet.sponsors.each do |sponsor| %>
| <%= sponsor.user_email %> |
<% end %>
<% else %>
<p>No Tengo Padinos =-( </p>
<% end %>
</td>
<td>
<%= link_to "Apadrinar", {:controller => "sponsors", :action => "new", :mascot => pet.id }%>
</td>
</tr>
<% end %>
I changed also my models.
sponsor.rb
class Sponsor < ApplicationRecord
belongs_to :user
belongs_to :pet
has_many :gifts
delegate :email, to: :user, prefix: true, allow_nil: true
end
user.rb
class User < ApplicationRecord
has_many :sponsors
has_many :pets, through: :sponsors
end
pet.rb
class Pet < ApplicationRecord
has_many :sponsors
has_many :users, through: :sponsors
end
And finally mi index on Controller.
sponsors_controller.rb
def index
# #sponsors = Sponsor.all
# #users = User.all
# #pets = Pet.all
# #pets_no_sponsors = Pet.where.not(id: Sponsor.select("pet_id")).select("id")
#pets = Pet.includes(:sponsors)
end
By making a delegation now I only make a query which sends everything necessary to my index.

Active Record query optimizations for two many-to-many join models

I am displaying the following table for a Product model in Rails 4 with ActiveRecord and Postgres SQL:
Here is my ERD, where I am using a couple of many-to-many models, each arrow here represents a one-to-many relationship:
impact_line_items are the row headings ("Total Impacts", "Source" ... etc.).
categories are the column headings ("Greenhouse Gases", "Energy Consumptions" ... etc.)
By adding entries to impact_line_items and categories, I can essentially extend the row and columns of the table
product_impact_line_item represents a reference to a row of data
impact_entry represents a cell of data
Question: Here is what I have in my modesl and products/show.html.erb, displaying the table. It is making 35 queries to get each ImpactEntry, and taking a while to load. I would like to optimize that down and looking for suggestions.
Models:
class Product < ActiveRecord::Base
...
has_many :product_impact_line_items, dependent: :destroy
has_many :impact_line_items, through: :product_impact_line_items
...
end
class ProductImpactLineItem < ActiveRecord::Base
belongs_to :product
belongs_to :impact_line_item
has_many :impact_entries, dependent: :destroy
has_many :categories, through: :impact_entries
def find_impact_entry(category)
impact_entries.find_by_category_id(category.id)
end
end
class ImpactEntry < ActiveRecord::Base
belongs_to :product_impact_line_item
belongs_to :category
end
class ImpactLineItem < ActiveRecord::Base
has_many :product_impact_line_items, dependent: :destroy
has_many :products, through: :product_impact_line_items
validates :name, uniqueness: true
end
class Category < ActiveRecord::Base
has_many :impact_entries, dependent: :destroy
has_many :categories, through: :impact_entries
validates :name, uniqueness: true
end
View:
<table id="impacts-table" class="table table-bordered table-medium">
<thead>
<tr>
<th class="col-md-2"></th>
<% Category.all.each do |category| %>
<th class="col-md-2"><%= category.name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% product_impact_line_items = #product.product_impact_line_items %>
<% product_impact_line_items.all.each do |product_impact_line_item| %>
<tr>
<th scope="row"><%= product_impact_line_item.impact_line_item.name %></th>
<% Category.all.each do |category| %>
<%# byebug %>
<td class="col-md-2"> <%= product_impact_line_item.find_impact_entry(category).value %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
Use ProductImpactLineItem.includes(:impact_line_item, :impact_entries) instead of ProductImpactLineItem.all (http://apidock.com/rails/ActiveRecord/QueryMethods/includes)
Change impact_entries.find_by_category_id(category.id) to impact_entries.find{ |ie| ie.category_id == category.id } so it won't call the DB each time
Use the bullet gem to detect n+1 queries in the future

Rails 4: sorting items in chronological order in nested loop

In my Rails 4 app, I have the following models:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
has_many :posts
has_many :comments, through: :posts
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Post < ActiveRecord::Base
belongs_to :calendar
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
delegate :first_name, to: :user, prefix: true
end
In my Calendar#Index view, I needed to display a list of all the comments (from all posts and all calendars) of a user.
So, I updated my Calendar#Index controller as follows:
def index
#user = current_user
#calendars = #user.calendars.all
end
And, in my Calendar#Index view, I wrote the following code:
<table id="my_todo_table">
<% #calendars.each do |calendar| %>
<% calendar.comments.each do |comment| %>
<tr>
<td><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span> <%= link_to calendar.name, calendar_path(calendar.id) %></td>
<td><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> <%= comment.post.subject %> </td>
<td><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <%= comment.user.first_name %></td>
<td><span class="glyphicon glyphicon-comment" aria-hidden="true"></span> <%= comment.body %></td>
</tr>
<% end %>
<% end %>
</table>
This is working well, except for one small detail: currently, comments are sorted by calendars, and then by post (which makes sense, given the code above).
Instead, I would like to have them sorted in chronological order, ie the most recent first first, regardless of which calendar or post it belongs to.
Is there a way to achieve this?
You could order by created_at datetime like so:
def index
#user = current_user
#calendars = Comment.where("user_id like ?", #user.id).order("created_at DESC")
end
You could probably do a new ActiveRecord query for the comments, since you are getting the comments through calendars, I would do something like this.
#comments = Comment.joins(post: :calendar).where(calendars: {id: #calendars.pluck(:id)}).distinct.order(created_at: :desc)
This will find all the comments that belong to a calendar and order the comments according to the created_at time stamp

Checking whether a user owns incoming and outgoing service booking requests

Use case: A user creates a service and can have many services. A user can book the service of another user through a servicebooking. A user can accept/decline the booking for their service from another user.
I am trying to display the outgoing service bookings made by the current user for another users services by checking a servicebookings model and also check/display the incoming bookings made by other users for the current users services on a myservicebookings page.
Myservicebookings view is as follows:
<h1>My Service bookings</h1>
<% if #owns_s %>
<table>
<tr>
<th><%= sortable "date" %></th>
<th><%= sortable "time" %></th>
<th><%= sortable "service name" %></th>
</tr>
<h4>Incoming requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
</table>
<%else%>
<%= "You have no incoming service booking requests"%>
<%end%>
<% if #owns_sb %>
<table>
<tr>
<th><%= sortable "date" %></th>
<th><%= sortable "time" %></th>
<th><%= sortable "service name" %></th>
</tr>
<h4>Outgoing requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
</table>
<%else%>
<%= "You have made no outgoing service booking requests"%>
<%end%>
<%= will_paginate #servicebookings %>
<%= link_to "Homepage", :controller => "welcome", :action => "index" %>
In my servicebookings controller I have the following to check whether a user owns a service or a servicebooking, currently it just returns all the services and servicebookings instead of displaying only the services created by the current user that another user has booking(incoming requests) and instead of displaying the services booked by the current user(outgoing requests). Can anyone give some tips here?? Thanks in advance guys, much appreciated.
def myservicebookings
#servicebookings = current_user.servicebookings.includes(:user).search(params[:search]).order(sort_column + " " + sort_direction).paginate(:per_page => 4, :page => params[:page])
owns_servicebooking = current_user.servicebookings.detect do |sb|
sb.user == current_user
end
owns_service = current_user.services.detect do |s|
s.user == current_user
end
#owns_sb = owns_servicebooking
#owns_s = owns_service
end
Servicebooking model:
class Servicebooking < ActiveRecord::Base
attr_accessible :service_id, :date, :time, :user_id, :service_name, :accept_booking
belongs_to :user
belongs_to :service
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Services model:
class Service < ActiveRecord::Base
attr_accessible :avatar, :avatar2, :avatar3, :avatar4, :name, :date_available, :time_available, :description, :price, :size, :company_name, :company_details
has_attached_file :avatar, :default_url => "/images/:style/missing.png"
has_attached_file :avatar2, :default_url => "/images/:style/missing.png"
has_attached_file :avatar3, :default_url => "/images/:style/missing.png"
has_attached_file :avatar4, :default_url => "/images/:style/missing.png"
belongs_to :user
belongs_to :event
has_many :comments, dependent: :destroy
has_many :servicebookings
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
has_many :through
I think you'll benefit from the has_many :through association
Your ServiceBooking model seems to be a join model, which needs to reference booking_id and service_id
The way you've got the system set up currently is to pull directly from this model. I think you'll be better to use it in conjunction with your other models. You'll literally just have to adapt your code slightly to affect this change:
#app/models/Service.rb
Class Service < ActiveRecord::Base
belongs_to :user
has_many :service_bookings
has_many :bookings, :through => :service_bookings
end
#app/models/Booking.rb
Class Booking < ActiveRecord::Base
belongs_to :user
has_many :service_bookings
has_many :services, :through => :service_bookings
end
#app/models/ServiceBooking.rb
Class Service < ActiveRecord::Base
belongs_to :service
belongs_to :booking
end
This will allow you to pull the actual data from the relative models, rather than just relying on the ServiceBooking model
The beauty of this is that because you can add extra attributes to join models in Rails, you'll be able to include other fields, such as user_id, inbound and outbound
Your Code
I'd do it like this:
#config/routes.rb
resources :users do
resources :bookings
resources :services
end
#app/controllers/users_controller.rb
def index
user = User.find(params[:id])
#bookings = user.bookings
#services = user.services
end
#app/controllers/bookings_controller.rb
def index
user = User.find(params[:user_id])
#bookings = user.bookings
end
#app/controllers/services_controller.rb
def index
user = User.find(params[:user_id])
#services = user.services
end
This will allow you to display the actual bookings / services for each user (all the services / bookings on the user index page; all relative records on bookings and services index actions)
Validation
If you follow these ideas, validation becomes somewhat more methodical
You can either use a before_create function on your ServiceBooking model (to check if the object matches the user_id), or you could perform some controller-based validation to see if the user_id is consistent
I should also mention that checking ownership of booking or service moves to the other models with this (I.E service belongs_to :user)

Rails table joins

I'm trying to figure out how to do a table join in one of my models.
There are points, questions, and users.
point.rb
class Point < ActiveRecord::Base
belongs_to :user
belongs_to :question
end
question.rb
class Question < ActiveRecord::Base
has_many :points
end
user.rb
class User < ActiveRecord::Base
In my Points controller I am doing this:
def index
#points = Point.all
#user_points = Point.where('user_id' => current_user)
end
And in my points/index view:
<% #user_points.each do |user_point| %>
<tr>
<td><%= current_user.name %></td>
<td><%= user_point.question_id %></td>
<td><%= user_point.correct_answer %></td>
<td><%= user_point.user_answer %></td>
</tr>
<% end %>
I need to access the name of each question in the questions table (I have the question id available in my view. I'm a n00b to rails, and can't figure out how to this with the documentation.
If you read my previous answer ignore it. I misread your question. This should work.
In your view:
<% user_points.questions.each do |question| %>
...Do whatever...
<% end %>
Take a look at the Rails Guides, especially these two:
http://guides.rubyonrails.org/association_basics.html
http://guides.rubyonrails.org/active_record_querying.html
I think you should be able to set this in your model:
class User < ActiveRecord::Base
has_many :points, :through => :questions
end
in your controller say #user_points = current_user.points
in your view. This should already work with your current code!
<% #user_points.each do |user_point| %>
<td><%= user_point.question.name %></td>
<% end %>

Resources