ActiveRecord Query User Lesson UserLesson - ruby-on-rails

I am trying to display the number of UserLesson.where(user_id: current_user_id) in the provided view (profiles_controller.rb). At the moment it displays the total UserLesson and not to individual user.
Here is my view:
<% #user.lectures.each do |lecture| %>
<div class="col-md-4 margin-s mb-60">
<div class="lecture-box">
<%= image_tag lecture.picture.url(:medium) %>
<%= lecture.lessons.size %>
<%= lecture.lessons.total_lesson %> #SHOULD BE HERE
<p class="pad5"><%= truncate(lecture.description, length: 80) %> </p>
<%= link_to "Start Now", lecture, class: "btn btn-primary" %>
</div> <!-- .lecture-box -->
</div> <!-- .col-md-4 -->
<% end %>
Here is my lesson.rb:
class Lesson < ActiveRecord::Base
has_one :lecture, through: :chapter
belongs_to :chapter
has_many :user_lessons
def self.total_lesson
Lesson.joins(:user_lessons).size
end
end
Other Models:
class UserLesson < ActiveRecord::Base
belongs_to :user
belongs_to :lesson
# validates_uniqueness_of :user_lesson, :scope => [:user, :lesson]
end
class Lesson < ActiveRecord::Base
has_one :lecture, through: :chapter
belongs_to :chapter
end
class User < ActiveRecord::Base
has_many :enrollments
has_many :user_lessons
has_many :lectures, through: :enrollments
accepts_nested_attributes_for :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :user
belongs_to :lecture
validates :lecture, uniqueness: { scope: :user, message: "should happen once per user" }
end
class Lecture < ActiveRecord::Base
belongs_to :category
has_many :lessons, through: :chapters, dependent: :destroy
has_many :chapters
belongs_to :teacher
# For course user relationship
has_many :enrollments
has_many :users, through: :enrollments
accepts_nested_attributes_for :enrollments
accepts_nested_attributes_for :chapters
end
class Chapter < ActiveRecord::Base
has_many :lessons
belongs_to :lecture
accepts_nested_attributes_for :lessons
end

To find the number of lessons belonging to a lecture in which the current user is related you can do this
<%= lecture.lessons.select{ |l| l.users.inculde? current_user }.count %>
but you have to add this to Lesson.rb:
has_many :users, through: :user_lessons

As you have #user in your view (which is the current_user), so you can just call the following in your view to get the total number of lessons for this user:
Lesson.joins(:user_lessons).where('user_lessons.user_id = ?', #user.id).count
To get the total number of lessons in this lecture for this user:
Lesson.joins(:user_lessons).joins(:lectures).where('user_lessons.user_id = ? AND lectures.id = ?', #user.id, lecture.id).count
where lecture is your local variable in your view file.
You should be able to call this directly from your view to get the desired count.

Related

How to change method name for has_through rails association?

I am trying to create two has many relations to the same destination tables but I am unable to override the default method name rails creates when defining the association.
The following is the case:
class User < ApplicationRecord
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :conferences, through: :conference_attendees, class_name: 'attending', dependent: :destroy
has_many :conferences, through: :conference_organizers, source: :conference, dependent: :destroy
class ConferenceOrganizer < ApplicationRecord
alias_attribute :organizers, :users
belongs_to :conference
belongs_to :user
end
class ConferenceAttendee < ApplicationRecord
belongs_to :conference
belongs_to :user
end
class Conference < ApplicationRecord
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :users, through: :conference_attendees, dependent: :destroy
has_many :organizers, through: :conference_organizers, source: :user, dependent: :destroy
I am trying to access all the conferences that a user attended and then all conferences that a user organized using something like following:
User.find(id: <id>).organizing
User.find(id: <id>).attending
but I am unable to and
User.find(id: <id>).conferences
defaults to organizing conferences. How do I access attending conferences?
Since you are doing a join on the same two tables it might be cleaner to do one join table and add a field called status to distinguish organizer from attendee. That way you can add other status values such as "presenter".
set up the table rails g model Registration user:references conference:references status:string; rake db:migrate
Set up the model associations:
# app/models/user.rb
has_many :registrations, dependent: :destroy
# app/models/conference.rb
has_many :registrations, dependent: :destroy
# app/models/registration.rb
belongs_to :user
belongs_to :conference
scope :attendees, -> { where( status: "Attendee") }
scope :organizers, -> { where( status: "Organizer") }
Then to show the attendees and organizers on the conference show page:
# app/controllers/conferences_controller.rb
def show
#conference = Conference.find(params[:id])
#attendee_registrations = #conference.registrations.attendees
#organizer_registrations = #conference.registrations.organizers
end
# app/views/conferences/show.html.erb
<h3>Organizers</h3>
<% #organizer_registrations.each do |registration| %>
<li><%= link_to(registration.user.name, registration.user) %></li>
<% end %>
<h3>Attendees</h3>
<% #attendee_registrations.each do |registration| %>
<li><%= link_to(registration.user.name, registration.user) %></li>
<% end %>
Then to show the conferences on the user's show page:
# app/controllers/users_controller.rb
def show
#user = User.find(params[:id])
#attending_registrations = #user.registrations.attendees
#organizing_registrations = #user.registrations.organizers
end
# app/views/users/show.html.erb
<h3>Conferences Organizing</h3>
<% #organizing_registrations.each do |registration| %>
<li><%= link_to(registration.conference.name, registration.conference) %><br>
Date: <%= registration.conference.date %></li>
<% end %>
<h3>Conferences Attending</h3>
<% #attending_registrations.each do |registration| %>
<li><%= link_to(registration.conference.name, registration.conference) %><br>
Date: <%= registration.conference.date %></li>
<% end %>
If you want two join tables on Users and Conferences you can set it up this way.
# app/models/user.rb
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :attending, through: :conference_attendees, source: :conference
has_many :organizing, through: :conference_organizers, source: :conference
# app/models/conference.rb
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :attendees, through: :conference_attendees, source: :user
has_many :organizers, through: :conference_organizers, source: :user
# app/models/conference_attendee.rb
belongs_to :user
belongs_to :conference
# app/models/conference_organizer.rb
belongs_to :user
belongs_to :conference
Then to show the attendees and organizers on the conference show page:
# app/controllers/conferences_controller.rb
def show
#conference = Conference.find(params[:id])
#attendees = #conference.attendees
#organizers = #conference.organizers
end
# app/views/conferences/show.html.erb
<h3>Organizers</h3>
<% #organizers.each do |user| %>
<li><%= user.name %></li>
<% end %>
<h3>Attendees</h3>
<% #attendees.each do |user| %>
<li><%= user.name %></li>
<% end %>
Then to show the conferences on the user's show page:
# app/controllers/users_controller.rb
def show
#user = User.find(params[:id])
#attending = #user.attending
#organizing = #user.organizing
end
# app/views/users/show.html.erb
<h3>Conferences Organizing</h3>
<% #organizing.each do |conference| %>
<li><%= link_to(conference.name, conference) %><br> Date: <%= conference.date %></li>
<% end %>
<h3>Conferences Attending</h3>
<% #attending.each do |conference| %>
<li><%= link_to(conference.name, conference) %><br> Date: <%= conference.date %></li>
<% end %>
That being said it may be cleaner to use one join table rather than two. I'll put that in a separate answer.

Rails: Use a collection of checkboxes to add/remove entries on a different table with ActiveRecord

I have three objects: Contact, Sector, and Contact_sector.
Contact contains an id and some other irrelevant (non-reference) columns
Sector contains an id and sector column with ~10 editable entries
Contact_sector has a contact_id reference and a sector_id reference. In my mind I imagine that every sector that applies to some contact can be found here, and if a sector is un-applied it is removed from here.
I want to have a collection of checkboxes in the contact _form formed from list of entries in :sectors, but updating the form with certain boxes ticked adds/removes entries from :contact_sectors.
Where am I going wrong?
UPDATED: Fixed strong_params to permit sectors, now I am unable to find the sectors by id ActiveRecord::RecordNotFound (Couldn't find Sector with ID=["1", ""] for Contact with ID=1)
Models:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :contact_sectors, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sectors, :reject_if => :all_blank, :allow_destroy => true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
def name_with_initial
"#{sector}"
end
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
View:
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
Controller
def edit
#contact.sectors.build
end
def contact_params
#Not sure if I need something like this or not
params['contact']['sectors'] = params['contact']['sectors']['id'].split(',')
params.require(:contact).permit(:firstname, :lastname,
contact_sectors_attributes: [:id],
sectors_attributes: [:_destroy, {:id => []}])
end
Instead of creating the join model explicitly you can just declare the relationship as has_many through: and let ActiveRecord handle the join model:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :sector,
reject_if: :all_blank, allow_destroy: true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
<%= form_for(#contact) do |f| %>
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
<% end %>
models
class Contact < ActiveRecord::Base
has_many :sectors, through: :contact_sectors
has_many :contact_sectors
accepts_nested_attributes_for :sectors
end
class Sector < ActiveRecord::Base
has_many :contacts, :through => :contact_sectors
has_many :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
view
<%= form_for(#contact) do |f| %>
<% Sector.all.each do |sector| %>
<%= check_box_tag "contact[sector_ids][]", sector.id, f.object.sectors.include?(sector) %>
<%= sector.sector %>
<% end %>
<% end %>
controller
def update
#To make sure it updates when no boxes are ticked
#contact.attributes = {'sector_ids' => []}.merge(params[:contact] || {})
end
def contact_params
params.require(:contact).permit(:firstname, :lastname, sector_ids: [])
end
Recommended reading:
http://millarian.com/rails/quick-tip-has_many-through-checkboxes/
Rails 4 Form: has_many through: checkboxes

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

How to use a many to many relationship

I have a many to many relationship between businesses and categories
class Business < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :categories
end
class Category < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :businesses
end
How would I create a business with 2 associated categories?
cat1 = Category.create(name: 'cat1')
cat2 = Category.create(name: 'cat2')
biz = Business.create(name: 'biz1'....
One option would be to use accepts_nested_attributes_for, like this:
class Business < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :categories
accepts_nested_attributes_for :businesses_categories
end
class Category < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :businesses
end
class BusinessesCategories < ActiveRecord::Base
accepts_nested_attributes_for :categories
end
You'd then be able to create your form like this:
<%= form_for #business do |f| %>
<%= f.fields_for :businesses_categories do |b| %>
<%= b.fields_for :categories do |c| %>
<%= c.text_field :cat %>
<% end %>
<% end %>
<% end %>
To do this, you'd have to build the category objects in the controller:
#app/controllers/businesses_controller.rb
def new
#business = Business.new
2.times do { #business.categories.build }
end
Or you'd have to call from a separate function to input the category data into their own table with the business_id set to the one you want
This is the Anothe way of specifying the many to many relationship
class Business < ActiveRecord::Base
has_many :business_categories
has_many :categories, through: :business_categories
end
class Category < ActiveRecord::Base
has_many :business_categories
has_many :businesses, through: :business_categories
end
class Business_category < ActiveRecord::Base
belongs_to :categories
belongs_to :businesses
end
see the link below:: http://guides.rubyonrails.org/association_basics.html

no method error in one to many relationship

models/user.rb
class User < ActiveRecord::Base
has_many :clubs, :dependent => :destroy
has_many :announcements, :dependent => :destroy
end
models/announcement.rb
class Announcement < ActiveRecord::Base
belongs_to :user
belongs_to :club
end
models/club.rb
class Club < ActiveRecord::Base
belongs_to :user
has_many :announcements, :dependent => :destroy
end
controllers/announcements/announcements_controller.rb
def index
#announcements = Announcement.find(:all, :include => [:user, :club])
end
Problem:
When i type this code,
views/announcements/index.html.erb
<% #announcements.each do |announcement| %>
<%= announcement.user.username %>
<% end %>
I get this error:
NoMethodError in Announcements#index
undefined method `username' for nil:NilClass
when i change code to this, it works.
<% #announcements.each do |announcement| %>
<%= announcement.club.user.username %>
<% end %>
Why is the first code is not working? What is the difference between these codes. Thanks.
It looks like you've got an announcement which has no user set, but does have a club which in turn does have a user. Perhaps consider adding a validation to require that the :user_id column on announcement is present.

Resources