In in a Rails 5 application we have a User and a Job models which have many skills through AttachedSkills.
AttachedSkills is a polymorphic table. It saves users or jobs skills.
User Model
class User < ApplicationRecord
has_many :attached_skills, as: :skillable, dependent: :destroy
has_many :skills, through: :attached_skills
end
Job Model
class Job < ApplicationRecord
has_many :attached_skills, as: :skillable, dependent: :destroy
has_many :skills, through: :attached_skills
end
Skills Model
class Skill < ApplicationRecord
has_many :attached_skills
end
AttachedSkill Model
class AttachedSkill < ApplicationRecord
belongs_to :skill
belongs_to :skillable, polymorphic: true
end
In the skills table let's say I have skills with id 640 and 641.
I need a query that fetches all users that have both skills. They must have at least these two skills.
How can I write such a query?
Ruby 2.6.6
Rails 5.2.5
Postregesql
Related
Here are the basic models without roles:
group model
class Group < ApplicationRecord
has_many :memberships
has_many :users, through :memberships
end
user model
class User < ApplicationRecord
has_many :memberships
has_many :groups, through :memberships
end
memberships model
class Membership < ApplicationRecord
belongs_to :group
belongs_to :user
end
Each group should have 1 (and only one) owner, multiple admins (assigned by the owner), and the rest general members. Also, the ownership role needs to be transferable. My options, as I see them:
Create a single attribute role on the membership table and assign it a string value of "owner", "manager", or "general". [bad]
Create multiple booleans for "owner", "manager", and "general" on the membership table. [bad]
Create a Role model/table with 1 column ("name") and 3 rows ("owner", "manager", "general") then update my models like so:
Role model
class Role < ApplicationRecord
has_many :memberships
has_many :users, through :memberships
has_many :groups, through :memberships
end
group model
class Group < ApplicationRecord
has_many :memberships
has_many :users, through :memberships
has_many :roles, through :memberships
end
user model
class User < ApplicationRecord
has_many :memberships
has_many :groups, through :memberships
has_many :roles, through :memberships
end
memberships model
class Membership < ApplicationRecord
belongs_to :group
belongs_to :user
belongs_to :role
end
Create separate arrays for the various roles directly on the group model. This seems really stupid, as updating a role would require the pushing and splicing of multiple arrays, and I would have to concatenate multiple arrays to present a simple list of memberships.
group model
class Group < ApplicationRecord
has_many :general_memberships, class_name: 'Membership'
has_many :admin_memberships, class_name: 'Membership'
has_one :owner_membership, class_name: 'Membership'
has_many :users, through :memberships
end
user model
class User < ApplicationRecord
has_many :general_memberships, class_name: 'Membership'
has_many :admin_memberships, class_name: 'Membership'
has_many :owner_memberships, class_name: 'Membership'
has_many :groups, through :memberships
end
memberships model
class Membership < ApplicationRecord
belongs_to :group
belongs_to :user
end
I know there are gems out there like CanCanCan (Rails 5 compatible?) and Groupify, but I want to understand all my options first. I think option #3 is probably my best best, at least without without utilizing a gem. Wondering what the community considers best practice for my scenario.
UPDATE
I ended up going with the following solution and have been very satisfied with it in a production app. Since a group can only have one owner, it made since to use a belongs_to relationship for that role.
For the Membership model, I took advantage of active record enum. Since the enum array only holds two values, it actually would have been more efficient to simply add a "manager" boolean to the membership model. True means manager, false means general. But I went with the enum approach, because I anticipate needing additional roles in the near future, and using enum they will be VERY easy to add.
group model
class Group < ApplicationRecord
belongs_to :owner, class_name: 'User'
has_many :memberships
has_many :users, through :memberships
end
user model
class User < ApplicationRecord
has_many :owned_groups, class_name: 'Group', :foreign_key => 'owner_id'
has_many :memberships
has_many :groups, through :memberships
end
membership model
class Membership < ApplicationRecord
belongs_to :group
belongs_to :user
enum role: [:general, :manager]
end
I have the following Rails models connecting teams and users:
class User < ActiveRecord::Base
has_many :memberships
has_many :teams, through: :memberships
end
class Team < ActiveRecord::Base
has_many :memberships
has_many :users
end
class Membership < ActiveRecord::Base
belongs_to :team
belongs_to :user
end
I want to implement a team manager via the existing memberships table.
Would it be a better idea to have a foreign key in the teams table like this:
class Team < ActiveRecord::Base
belongs_to :manager_membership, class_name: 'Membership'
has_one :manager, through: :manager_membership, source: :user
# ...
end
Or would it be better to have an extra column (with unique index) on memberships like this:
class Team < ActiveRecord::Base
has_one :manager_membership, -> where { manager: true }, class_name: 'Membership'
has_one :manager, through: :manager_membership, source: :user
# ...
end
I wonder if having foreign keys going both ways from team to a membership and back is weird? Or maybe it doesn't make much difference either way?
Based on the comments, you should be able to use a single table inheritance for both your users and managers. That would mean you get to use the same membership relationship on both models.
class User < ActiveRecord::Base
has_many :memberships
has_many :teams, through: :memberships
end
class Manager < User
# your code
end
You can now use Manager wherever needed, and add any validations directly on that model. Remember to add a type column to your User table if you decide to go this route.
EDIT
Looking at this again, you will need to have a separate table to keep the relationship between your Manager's team and the actual team they manage clear. Yourteam` should be something like:
class Team < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
has_many :teams_managers
has_many :managers, through: :teams_managers
end
class Manager < User
has_many :teams_managers
# note here we're aliasing for clarity and specifying a source
has_many :managed_teams, through: :teams_managers, source: :team
end
# this is your association model for managers <-> teams
class TeamsManager < ActiveRecord::Base
belongs_to :manager
belongs_to :team
end
# so now you could call something like this
manager = Manager.find 1
manager.managed_teams =>[...]
manager.teams =>[...]
You'd have to make the corresponding migrations to instantiate your TeamsManagers table as well.
Would association person and company through employment AND through interviewing be bad practice?
Why, specifically?
And does that answer apply to databases in general, not just rails?
Example:
employment.rb
class Employment < ActiveRecord::Base
belongs_to :people
belongs_to :companies
end
interview.rb
class Interview < ActiveRecord::Base
belongs_to :people
belongs_to :companies
end
person.rb
class Person < ActiveRecord::Base
has_many :employments
has_many :interviews
has_many :companies, through: :employments
has_many :companies, through: :interviews
end
company.rb
class Company < ActiveRecord::Base
has_many :employments
has_many :interviews
has_many :companies, through: :employments
has_many :companies, through: :interviews
end
Person and Company are associated through Employment, but also redundantly through Interview.
There's nothing wrong with having two models having multiple associations, including multiple associations of the same type. You will want to give the associations unique names, though - otherwise when you called (for instance) #person.companies, you wouldn't know what you were going to get - companies through employments, or companies through interviews.
I believe this question has a decent example: ruby on rails has_many :through association which has two columns with same model
I am new to Rails and designing an application where domain entities are related like:
User belongs to many Organizations
Organization have many Users
Organization have many Applications
Application is developed by an Organization
Application is developed by many Users(developers) of same organization
User is developer of many Applications
Here is the mapping of above relations
class Organization < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :organizations, through: :memberships
has_many :application_developers
has_many :applications, through: application_developers
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :organization
end
class Application < ActiveRecord::Base
belongs_to :organization
has_many :application_developers
has_many :memberships, through: application_developers
end
class ApplicationDeveloper < ActiveRecord::Base
belongs_to :membership
belongs_to :application
end
would anyone please verify if above mapping is correct? Please give your feedback.
It would be very appreciable if you have design better than above.
If every user from the organization is not working on an app, and it is only certain users, then I would keep the ApplicationDeveloper table, but change
belongs_to :membership
to
belongs_to :user
In Rails 3 with ActiveRecord, I have 2 models (Users and Tasks). These models are linked together with a has_many :through association on another model, Assignments. How can I find all Tasks that are NOT associated to a particular user?
class User < ActiveRecord::Base
has_many :assignments
has_many :tasks, :through => :assignments
end
class Tasks < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
end
class Assignments < ActiveRecord::Base
belongs_to :users
belongs_to :tasks
end
Short 'n sweet:
Task.all - user.tasks
Avoid loading user tasks:
Task.where('id not in (?)', user.task_ids)
I couldn't figure out how to do it with an outer join in AR.
I'm going to assume you want those tasks without any associated user, rather than not associated to a user in particular.
Tasks.joins('left outer join assignments on assignments.task_id = tasks.id').where('assignments.* is null')