Creating a custom validation across two HMT associations - ruby-on-rails

I have two HMT associations. The first is between my Artist and Album models, basically signifying that multiple artists can belong to an album. The other association is setup between my Artist and Track models via TrackFeature. The purpose of this association is add a featured artist to a track.
I'd like to setup a validation that none of the possible album artists cannot be featured artists on any tracks pertaining to that particular album. I know that this validation would need to be on the join model. The problem is that what I have now doesn't seem to work. What do I need to change in order for this to successfully validate?
class Artist < ActiveRecord::Base
has_many :album_artists
has_many :albums, through: :album_artists
has_many :track_features
has_many :tracks, through: :track_features
end
class Album < ActiveRecord::Base
has_many :album_artists
has_many :artists, through: :album_artists
end
class AlbumArtist < ActiveRecord::Base
belongs_to :album
belongs_to :artist
end
class Track < ActiveRecord::Base
has_many :track_features
has_many :featured_artists, through: :track_features
belongs_to :album
end
class TrackFeature < ActiveRecord::Base
belongs_to :track
belongs_to :featured_artist, class_name: "Artist", foreign_key: :artist_id
validate :track_artist
private
def track_artist
if track.album.artists.exists?(name: artist.name)
track.errors[:base] << "Album artist cannot be a featured artist"
end
end
end

Related

Rails: How to group by nested association?

I have a Follow model with user_id and track_id. The Track model has an artist_id field.
What I want to do is count which artists have the most followers, but since users follow "tracks" and not "artists", I need to figure out how to do a count through the tracks.
So, what I was thinking was to do some sort of group by on a nested association. i.e. Group the Follow records by "track -> artist_id", somehow.
Then I could count the number of users for each.
Is that even possible? Is there more info that would be useful here?
You need to use has_many :through to establish the Artist <-> Tracks <-> User relationship.
class Artist < ApplicationRecord
has_many :tracks
has_many :users, through: :tracks
end
class Follow < ApplicationRecord
belongs_to :user
belongs_to :track
end
class Track < ApplicationRecord
belongs_to :artist
has_and_belongs_to_many :users, join_table: :follows
end
class User < ApplicationRecord
has_and_belongs_to_many :tracks
has_many :artists, through: :tracks, join_table: :follows
end
Then Rails can take care of the joins between Artist and User.
Artist.includes(:users).group(:id).count("users.id")

Is this database relation schema correct?

I made my first project and got into problem that I couldn't get the right category_information values for specific competitions category through relations. So I started thinking that this could be the wrong schema for this task, so my question - is it actually wrong?
Current Scheme:
Assuming the following relationships between models from your image.
class Competition < ApplicationRecord
has_many :categories
has_many :informations
has_many :category_informations, through: :categories
end
class Category < ApplicationRecord
belongs_to :competetion
has_many :category_informations
has_many :information, through: :category_informations
end
class CategoryInformation
belongs_to :catagory
belongs_to :information
end
class Information < ApplicationRecord
belongs_to :competetion
has_many :category_informations
has_many :catagory, through: :category_information
end
Model can relates with one_to_many_to_many using :through option
It explains a association used to set up a many-to-many connection with another model.
you can get the category_informations from competition like
Competition.first.category_informations
It is all for doing! Pretty good, right?
And you could do get information from category too
Category.first.informations
Actually wrong schema doesn't exist, just there exists some wrong association description.
You can get more usage to use association from docs at 2.3 section and 4.3 section
Assuming the following relationships between tables,
A competition has_many categories,
A competition has_many information,
A category has_many information,
A category has_many competition,
An information has_many category
You can use has_many_through relationships
class Category < ApplicationRecord
has_many :category_competitions
has_many :competitions, through: :category_competition
has_many :category_informations
has_many :informations, through: :category_informations
end
class Information < ApplicationRecord
has_many :category_informations
has_many :categories, through: :category_informations
end
class Competition < ApplicationRecord
has_many :category_competition
has_many :categories, through: :category_competitions
end
class CategoryCompetition < ApplicationRecord
belongs_to :category
belongs_to :information
end
class CategoryInformation < ApplicationRecord
belongs_to :category
belongs_to :information
end
By this way you can access,Categories of a particular competition by #competition.categories
This article might be helpful for you to understand associations better
https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/

when to use polymorphic associations vs STI vs another table in rails

I have taken a look at Polymorphic associations and STI but I don't really know if they apply to my specific use case. In my app I have the following relevant classes:
class Restaurant < ApplicationRecord
belongs_to :owner, class_name: "User"
has_many :menus
has_many :dishes
has_many :categories
has_many :bookings
has_many :simple_bookings
mount_uploader :photo, PhotoUploader
end
class Dish < ApplicationRecord
has_many :menu_dishes
has_many :menus, through: :menu_dishes
belongs_to :category
belongs_to :restaurant
mount_uploader :photo, PhotoUploader
end
class Menu < ApplicationRecord
belongs_to :restaurant
has_many :bookings
has_many :categories
has_many :menu_dishes
has_many :dishes, through: :menu_disheshas_many :menu_dishes
end
class MenuDish < ApplicationRecord
belongs_to :dish
belongs_to :menu
end
So far the user flow is the following:
A restaurant owner signs up and creates restaurant dishes. Then this owner creates a menu containing different MenuDishes.
Afterwards, users can search for different menus and book them.
My problem is the following:
I want to implement a feature where owners can add up to 3 MenuDishOptions (that belong to MenuDishes) so that a user can change each MenuDish with the available MenuDishOption for that MenuDish.
In other words, I want each MenuDish to contain many MenuDishOptions. Once a MenuDishOption is selected, the MenuDish passes to be a MenuDishOption and the selected MenuDishOption to a MenuDish.
Therefore the MenuDishOption class would look something like this:
class MenuDishOption < ApplicationRecord
belongs_to :menu_dish
has_one :dish
belongs_to :menu, through: menu_dishes
end
The MenuDish class would be updated to:
class MenuDish < ApplicationRecord
has_many :menu_dish_options
belongs_to :menu
belongs_to :dish
end
Please let me know if I need to share more info and thanks a million to anyone that takes the time to help this newbie.

Validate object based on the owner its parent

I have a has_many through association setup between my artist and album models. To add to this an album has_many tracks. The tracks also have a has_many through association between artists (i.e. featured artist) where the Feature model serves as the join table.
I want to prevent the album artist(s) from being a featured artist on a track. So for instance:
album = Album.find_by(name: "Justified")
track = Album.track.first
artist = Artist.find_by(name: "Justin Timberlake")
track.featured_artists << artist
# ^^ Should raise error since Justin Timberlake is the album's artist
Model Setup
class Album < ActiveRecord::Base
has_many :album_artists
has_many :artists, through: :album_artists
end
class Track < ActiveRecord::Base
belongs_to :album
has_many :features
has_many :featured_artists, through: :features, class_name: "Artist", foreign_key: "artist_id"
end
class AlbumArtist < ActiveRecord::Base
belongs_to :album
belongs_to :artist
validates_uniqueness_of :artist_id, scope: :album_id
end
class Feature < ActiveRecord::Base
belongs_to :track
belongs_to :featured_artist, class_name: "Artist", foreign_key: "artist_id"
end
class Artist < ActiveRecord::Base
has_many :album_artists
has_many :albums, through: :album_artists
has_many :features
has_many :tracks, through: :features
end
Is there a way to accomplish this using one of the out-of-the-box validation methods? If not how would I go about in creating a custom validator without writing a ridiculously long lookup chain to the album's owner, assuming the validator is in the Feature model?
You can test that the intersection (&) of the two artists array is empty.
class Track < ActiveRecord::Base
belongs_to :album
has_many :features
has_many :featured_artists, through: :features, class_name: "Artist", foreign_key: "artist_id"
validate :featured_artists_cannot_include_album_artists
def featured_artists_cannot_include_album_artists
if (album.artists & featured_artists).present?
errors.add(:featured_artists, "can't include album artists")
end
end
end

Rails belongs_to_many

I'm a beginner in Rails and I have a problem with ActiveRecords associations.
I'm creating simple car rental service and I made the following associations:
class Client < ActiveRecord::Base
has_many :rentals
has_many :bookings
has_many :cars, :through => :rentals
has_many :cars, :through => :bookings
end
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_one :car
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_one :car
end
What I need is to have a car belonging to many bookings and rentals while every booking and rental can have only one car assigned.
class Car < ActiveRecord::Base
# belongs_to_many :bookings
# belongs_to_many :rentals
end
How should I do that?
If a car can have many bookings/rentals, but a booking/rental can only have one car, you're looking at a classic belongs_to/has_many situation. It looks like you're being tripped up by the distinction between belongs_to and has_one -- it's not a grammatical one, but a matter of where the foreign key column is located in your database.
belongs_to: "I am related to exactly one of these, and I have the foreign key."
has_one: "I am related to exactly one of these, and it has the foreign key."
has_many: "I am related to many of these, and they have the foreign key."
Note that has_one and has_many both imply there's a belongs_to on the other model, since that's the only option where "this" model has the foreign key. Note also that this means has_one should only be used when you have a one-to-one relationship, not a one-to-many.
Taking this into consideration, I would replace the has_one :car with belongs_to :car in both your Rental and Booking models, and place has_many :bookings and has_many :rentals in your Car model. Also ensure that your rentals and bookings tables have a car_id column; there should be no rental- or booking-related columns in your cars table.
Yes, there is a "belongs_to_many" in Rails, sort of. It's a little more work and you can't use generators with it. It's called a polymorphic association.
Even though you could make a car have many bookings & rentals, you could associate the car by making it belong to a polymorph such as rentable_vehicle. Your code would look like this
class Car < ActiveRecord::Base
belongs_to :rentable_vehicle, polymorphic: true
end
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_many :cars, as: :rentable_vehicle
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_many :cars, as: :rentable_vehicle
end
You can't do belongs_to_many. The closest you can really get is has_and_belongs_to_many, but I'm not sure that's what you want here - unless you can have multiple cars per rental/booking. Check out the guide for a full explanation.
I'd change it up like this:
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :car
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :car
end
class Car < ActiveRecord::Base
has_many :bookings
has_many :rentals
end
Also, I don't know how your rentals relate to bookings, but my immediate thought is that there should be some relationship between the two, because you probably can't have a rental without booking it, right?

Resources