How to get relation attributes joined together in rails? - ruby-on-rails

I have this type of relation in my models:
class Movie < ActiveRecord::Base
  has_many :movie_genres
  has_many :genres, through: :movie_genres
end
 
class MovieGenre < ActiveRecord::Base
  belongs_to :movie
  belongs_to :genre
end
 
class Genre < ActiveRecord::Base
  has_many :movie_genres
end
So I am assigning multiple genres to a movie. Also the model MovieGenre has additional columns/attributes like "sort_order" which descripe the order/priority of an genre for a move. So far I can retrieve the genres and I can retrieve the relations (MovieGenre-records) by simply calling #movie.genres or *#movie.movie_genres* but how can I join those two informations and retrieve them together?
So far I saw myself forced to manually search the *movie_genre* record for every genre. Is there a way so that I can get those together so that I have for example the genre.name available together with movie_genre.sort_order?
What would be the rails way or maybe there even exists some rails magic for that?

You've got your associations wrong:
class Movie < ActiveRecord::Base
has_many :movie_genres
has_many :genres, through: :movie_genres
end
class MovieGenre < ActiveRecord::Base
belongs_to :movie
belongs_to :genre
end
class Genre < ActiveRecord::Base
has_many :movie_genres
has_many :movies, through: :movie_genres
end
I haven't tested this, but you must use singulars on the through model associations. (And I've added the "has_many :movies" on Genre for symmetry.
And to read the attributes:
#movie.movie_genres.count # => 3
#movie.movie_genres.first # => MovieGenre
#movie.movie_genres.first.genre # => Genre

Related

Extracting data using rails query from a join table

I have users table, books table and books_users join table. In the users_controller.rb I am trying extract the users who have filtered_books. Please help me to resolve that problem.
user.rb
has_many :books_users, dependent: :destroy
has_and_belongs_to_many :books, join_table: :books_users
book.rb
has_and_belongs_to_many :users
books_user.rb
belongs_to :user
belongs_to :book
users_controller.rb
def filter_users
#filtered_books = Fiction.find(params[:ID]).books
#users = **I want only those users who have filtered_books**
end
has_and_belongs_to_many does not actually use a join model. What you are looking for is has_many through:
class User < ApplicationRecord
has_many :book_users
has_many :books, through: :book_users
end
class Book < ApplicationRecord
has_many :book_users
has_many :users, through: :book_users
end
class BookUser < ApplicationRecord
belongs_to :book
belongs_to :user
end
If you want to add categories to books you would do it by adding a Category model and another join table. Not by creating a Fiction model which will just create a crazy amount of code duplication if you want multiple categories.
class Book < ApplicationRecord
has_many :book_users
has_many :users, through: :book_users
has_many :book_categories
has_many :categories, through: :book_categories
end
class BookCategory < ApplicationRecord
belongs_to :book
belongs_to :category
end
class Category < ApplicationRecord
has_many :book_categories
has_many :books, through: :book_categories
end
If you want to query for users that follow a certain book you can do it by using an inner join with a condition on books:
User.joins(:books)
.where(books: { title: 'Lord Of The Rings' })
If you want to get books that have a certain category:
Book.joins(:categories)
.where(categories: { name: 'Fiction' })
Then for the grand finale - to query users with a relation to at least one book that's categorized with "Fiction" you would do:
User.joins(books: :categories)
.where(categories: { name: 'Fiction' })
# or if you have an id
User.joins(books: :categories)
.where(categories: { id: params[:category_id] })
You can also add an indirect association that lets you go straight from categories to users:
class Category < ApplicationRecord
# ...
has_many :users, though: :books
end
category = Category.includes(:users)
.find(params[:id])
users = category.users
See:
The has_many :through Association
Joining nested assocations.
Specifying Conditions on Joined Tables
From looking at the code i am assuming that Book model has fiction_id as well because of the has_many association shown in this line Fiction.find(params[:ID]).books. There could be two approaches achieve this. First one could be that you use #filtered_books variable and extract users from it like #filtered_books.collect {|b| b.users}.flatten to extract all the users. Second approach could be through associations using fiction_id which could be something like User.joins(:books).where(books: {id: #filtererd_books.pluck(:id)})

Rails type of relation between models

I have 3 models: User, Order and Car and I have question because I don't know what relationships between these models will be the best. Only requirement is that only one car per user in order.
A user can have many orders, and therefore, many cars through those orders.
class User < ActiveRecord::Base
has_many :orders
has_many :cars, through: :orders
end
An order belongs to a user and a car.
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :car
end
A car has one order.
class Car < ActiveRecord::Base
has_one :order
end
So you need one on one relationship between order and car and then back to order and customer one relationship. Something below should do the trick.
class Car < ActiveRecord::Base
has_one :order
has_one :customer, through: :order
end
class Order < ActiveRecord::Base
belongs_to :car
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_one :order
has_one :car , through: :order
end
But i will suggest the has_many relationship by the looks of the model name, but you know better your problem than me.

What references, foreign_keys do I need to add for these associations?

I have these associations in my Rails 4.2 app. I don't understand how to setup the proper references/foreign_keys between instructors and courses.
So far both instructors and courses tables have a local_id (local reference).
Local is a training center they both belong to.
class Local < ActiveRecord::Base
has_many :instructors
has_many :courses
end
class Instructor < ActiveRecord::Base
belongs_to :local
has_many :courses, through: :locals
end
class Course < ActiveRecord::Base
belongs_to :local
has_many :instructors, through: :locals
end
Do I add a foreign_key to the courses table? Like this:
add_foreign_key :courses, :instructors
I read something about when having many to many associations we need a "join table" cause we need to store many ids. I guess Local is just that in this case.
Or do I need another table(Model) that belongs_to :instructor, :course?
Here is how I would set it up for maximum flexibility.
Instructors and Courses
Lets set up a many to many relationship with a join model which we call Employment.
We can generate the model with:
rails g model employment instructor:belongs_to course:belongs_to
Which will give us this:
class Employment < ActiveRecord::Base
belongs_to :instructor
belongs_to :course
end
employments will have the instructor_id and course_id foreign keys. This lets us assign any number of instructors to a course and vice versa.
So let's put the join model to use:
class Instructor < ActiveRecord::Base
has_many :employments
has_many :courses, through: :employments
end
class Course < ActiveRecord::Base
has_many :employments
has_many :instructors, through: :employments
end
Venues and Courses
I would recommend you rename your Local model Venue as it's the common english term for the place where a course or event is held. Locale is awkward since it collides with a separate concept in web applications and may cause name clashes.
And we should probably set it up as many to many to account for the complexities of real life.
So again:
rails g model booking venue:belongs_to course:belongs_to
class Booking < ActiveRecord::Base
belongs_to :venue
belongs_to :course
end
bookings will have the venue_id and course_id foreign keys.
class Venue < ActiveRecord::Base # was Local
has_many :bookings
has_many :courses, through: :bookings
end
class Course < ActiveRecord::Base
# ...
has_many :bookings
has_many :venues, through: :bookings
end
More reading:
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
http://blog.flatironschool.com/why-you-dont-need-has-and-belongs-to-many/

Should this has_many :through relationship be polymorphic or not?

I have a many-to-many relationship setup for Teachers and Classrooms via has_many :through:
class Teacher < ActiveRecord::Base
has_many :classrooms, :through => :classroom_memberships
end
class Classroom < ActiveRecord::Base
has_many :students
has_many :teachers, :through => :classroom_memberships
end
class ClassroomMemberships < ActiveRecord::Base
belongs_to :teacher
belongs_to :classroom
end
Currently, Students can only belong to one Classroom:
class Student < ActiveRecord::Base
belongs_to :classroom
end
Now I have the need to track historical classroom memberships for students, creating a second many-to-many relationship for classrooms. So, while a student can only belong to one classroom at a time, I need to know that last year, student A belonged to classroom B.
I'm thinking I have two viable options:
1.) Make the classroom_memberships association polymorphic so I'd have a classroomable_id and classroomable_type that would point to either a teacher OR a student.
2.) Simplify things and add another foreign key to ClassroomMemberships called student_id, in which case, for a given row, either student_id OR teacher_id would have a value.
Which is the better option?
I would probably go the route of:
class Course < ActiveRecord::Base
# like "MATH 100"
has_many :sections
has_many :teachers, :through => :sections
end
class Term < ActiveRecord::Base
# like "Fall 2015"
has_many :sections
end
class Teacher < ActiveRecord::Base
has_many :sections
has_many :courses, :through => :sections
end
class Section < ActiveRecord::Base
# a course, in a term, taught by a teacher, with registered students
belongs_to :term
belongs_to :course
belongs_to :teacher
has_many :registrations
has_many :students, :through => :registrations
end
class Registration < ActiveRecord::Base
# a student in a specific section
belongs_to :section
belongs_to :student
end
class Student < ActiveRecord::Base
# a student's registrations are their course history
has_many :registrations
has_many :sections, through :registrations
end
As a start, since this is a fairly basic modeling of an educational system.
It sounds like maybe you want a ClassroomMembershipHistory model.
Something like
class ClassroomMembershipHistory < ActiveRecord::Base
belongs_to :student
belongs_to :classroom
end
with a year attribute, stored however is easiest to query for your use case.

Associations two-many-to-many

I don't fully understand how associations work.
I have 3 models:
Movie (:about, :title, :url, :actors, :uploader)
Actor (:birth, :name)
Relationships (:actor_id, :film_id)
Relationships is the association between Movies and Actors, so "which actor play in which movie". My teacher told me I can make it easier with http://railscasts.com/episodes/47-two-many-to-many but I don't know how to use that, any idea?
I would imagine you want something like:
class Movie < ActiveRecord::Base
has_many :actors, :through => :relationships
end
class Relationship < ActiveRecord::Base
belongs_to :movie
belongs_to :actor
end
class Actor < ActiveRecord::Base
has_many :movies, :through => :relationships
end
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
Or alternatively, if you don't feel you have to define the relationship class explicitly, you can simply use has_and_belongs_to_many:
class Movie < ActiveRecord::Base
has_and_belongs_to_many :actors
end
class Actor < ActiveRecord::Base
has_and_belongs_to_many :movies
end

Resources