Finding all Admins using Rails Associations - ruby-on-rails

How do I find all admins using Rails 6 active record associations?
I have the following classes:
class Group < ApplicationRecord
has_many :relationships, dependent: :destroy
has_many :users, through: :relationships
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :relationships, dependent: :destroy
has_many :groups, through: :relationships
end
class Relationship < ApplicationRecord
belongs_to :user
belongs_to :group
validates :user_id, presence: true
validates :group_id, presence: true
validates :admin, inclusion: [true, false]
I'd like to add admins as a has_many relationship in Group.
Here's my first attempt at doing so:
has_many :admins, class_name: "User", through: :relationships
How would I filter those users whose relationship to the group has the admin attribute set to true?

Associations can be scoped using the normal query syntax.
class Group < ApplicationRecord
...
has_many :admins,
-> { where({ relationships: { admin: true} }) },
through: :relationships,
source: :user
end
source is necessary because otherwise it will try to do the has_many through Relationships.admins.
group.admins is equivalent to group.users.where({ relationships: { admin: true} }).

You could've modified your Group model to something like:
class Group < ApplicationRecord
scope :admins, -> { where(relationships: { admin: true }) }
has_many :relationships, dependent: :destroy
has_many :users, through: :relationships
end
From it you can use Group.joins(:users).admins to get groups with assoiciated admins.

Related

How do target polymorphic model through many models

I have 4 classes:
class User < ApplicationRecord
has_many :memories
has_many :playlists
has_many :items, as: 'playlist_items', through: :playlists
has_many :items, as: 'memory_items', through: :memories
end
class Item < ApplicationRecord
belongs_to :itemable, polymorphic: true, optional: true
end
class Playlist < ApplicationRecord
belongs_to :user
has_many :items, as: :itemable, dependent: :destroy
accepts_nested_attributes_for :items, allow_destroy: true
end
class Memory < ApplicationRecord
belongs_to :user
has_many :items, as: :itemable, dependent: :destroy
accepts_nested_attributes_for :items, allow_destroy: true
end
I would like to be able to get from current_user to the items of either type, i.e. Memory or Playlist. But right now I can only get to 1 set.. if I have this in User:
has_many :items, through: :playlists
I can't currently figure out how to do both in a list. 'As' seems to have no effect. Any suggestions would be very helpful?
You can't have both associations with the same name, you can specialize specifying the source. Try like this:
class User < ApplicationRecord
has_many :memories
has_many :playlists
has_many :playlist_items, through: :playlists, source: :items
has_many :memory_items, through: :memories, source: :items
end
Then of course you use user.playlist_items and user.memory_items instead.

Uniqueness validation not working on has_many :though association on rails 5

Models
class User < ApplicationRecord
has_many :memberships
has_many :pages, through: :memberships
end
class Page < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
end
class Membership < ApplicationRecord
belongs_to :user
belongs_to :page
validates :page_id, uniqueness: { scope: :user_id}
end
Am able to create multiple user on pages and pages on user, it's like the validation isn't getting called.
To trigger the validations in a associated model you need to use validates_associated:
class User < ApplicationRecord
has_many :memberships
has_many :pages, through: :memberships
validates_associated :memberships
end
class Page < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
validates_associated :memberships
end
class Membership < ApplicationRecord
belongs_to :user
belongs_to :page
validates :page_id, uniqueness: { scope: :user_id}
end
This can be a real gotcha as validations on associations are not called when they are created implicitly.
Additionally its a good idea to create a compound database index which prevents possible race conditions:
class AddCompoundIndexToMemberships < ActiveRecord::Migration[5.0]
def change
add_index :memberships, [:page_id, :user_id], unique: true
end
end
This ensures on the DB level that no two identical rows can be created.

Get all related instances from has_many through relationship of a polymorphic class

I'm trying to create way for Users to Like content in my project. To do this I set up a polymorphic relationship as follows
Likes table
table "likes", force: :cascade do |t|
t.integer "likeable_id"
t.string "likeable_type"
t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
models
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :newsletters, through: :likes, source: :likeable, source_type: "newsletter"
has_many :gift_cards, through: :likes, source: :likeable, source_type: "gift_card"
has_many :stories, through: :likes, source: :likeable, source_type: "story"
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
end
class Newsletter < ActiveRecord::Base
has_one :like, as: :likeable, dependent: :destroy
has_one :user, through: :like, source: :likeable
end
class GiftCard < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes, source: :likeable
end
class Story < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes, source: :likeable
end
tldr: a User has many Likes and many Newsletters, GiftCards, and Stories through those Likes.
My question, with this structure, whats the best way to get everything a User has liked?
For me this is extremely hard to follow for a domain with is in reality is quite straightforwards. Perhaps you have a bigger reason to implement it is this way but here's my suggestion for the User Like and GiftCard models (which may or may not help!).
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :liked_giftcards, through: :likes, source: :giftcard
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :giftcard
end
class GiftCard < ActiveRecord::Base
has_many :likes, dependent: :destroy
end
class Story < ActiveRecord::Base
...same as GiftCard
end
Personally I would find this easier to follow. It provides easy access to everything a user has liked through user.likes and allows you to look at each category individually through user.liked_giftcards etc.
Ended up figuring it out. Just had some small syntactical errors
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :newsletters, through: :likes, source: :likeable, source_type: "Newsletter"
has_many :gift_cards, through: :likes, source: :likeable, source_type: "GiftCard"
has_many :stories, through: :likes, source: :likeable, source_type: "Story"
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
end
class Newsletter < ActiveRecord::Base
has_one :like, as: :likeable, dependent: :destroy
has_one :user, through: :like
end
class GiftCard < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes
end
class Story < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes
end

rails - HasManyThroughAssociationNotFoundError on plural has_many

I have the following models:
User
Team
UserTeam
There are users and there are teams. Each team has 0 or more users, the join table is UserTeams
I want to get all teams and for each team get its users. Initially I tried for one team and get its users, like this
Team.find('759ccbb7-2965-4558-b254-3e437ca721aa').users
but rails complains about this with:
Could not find the association :user_team in model Team
This is how my Team model looks like:
class Team < ActiveRecord::Base
has_many :users, through: :user_team
has_many :user_teams, dependent: :destroy
accepts_nested_attributes_for :user_teams, :reject_if => lambda { |a| a[:user_id] == '0' }
validates :name, presence: true
end
Team model:
class UserTeam < ActiveRecord::Base
belongs_to :team
belongs_to :user
validates :user_id, presence: true
end
Weird enough, if I change Team model like this:
has_many :user_team
using the singular word, it works, but I read that it has to be pluralized
Just change this:
has_many :users, through: :user_team
has_many :user_teams, dependent: :destroy
to
has_many :user_teams, dependent: :destroy
has_many :users, through: :user_teams
The through part should use existing association.
In your code, you specified through :user_team, but you don't have has_many :user_team, that's why rails complains.
The solution was to put these inside User model:
has_many :user_teams
has_many :teams, through: :user_teams
I forgotten about these. Now it works

Rails custom validation on has_many association

Given the following models:
Business
class Business < ActiveRecord::Base
## OWNERS / EMPLOYEES
has_many :business_users, as: :business, dependent: :destroy
has_many :users, through: :business_users
accepts_nested_attributes_for :business_users, allow_destroy: true, reject_if: lambda { |a| a[:user_id].blank? || a[:role].blank? }
end
BusinessUser
class BusinessUser < ActiveRecord::Base
belongs_to :business, polymorphic: true
belongs_to :user
enum role: {:owner => 0, :employee => 1, :admin => 2}
validates_presence_of :user
validates :user, uniqueness: { scope: :business, message: "Each user can only have one role" }
end
User
class User < ActiveRecord::Base
has_many :business_users
has_many :businesses, through: :business_users, source_type: Business, source: :business
end
How can i make sure that each business has at least one business_user with role :owner?

Resources