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?
Related
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.
I'm making a rails marketplace app for uni where Users can be matched with specific products based on their request.
Users can list products that have specific categories.
Users can also list Requests where they can specify what products they're looking and their categories.
The aim is to match the request to a particular product based on the matching categories
Here are my models
class Product < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :product_categories
has_many :categories, through: :product_categories
validates :user_id, presence: true
end
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through: :product_categories
validates :name, presence: true, length: { minimum: 3, maximum: 25}
validates_uniqueness_of :name
end
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category
end
class Request < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :request_categories
has_many :categories, through: :request_categories
validates :user_id, presence: true
end
class RequestCategory < ApplicationRecord
belongs_to :request
belongs_to :category
end
I was thinking of creating a new model called Match to bring together the product and categories or is it easier to match it in the request?
In my mind, your new Match class would essentially be a join table for a has_many :through association. Assuming that you're implementing an asynchronous worker (e.g. Sidekiq / ActiveJob) to go through and make "matches", you'll want to connect matches to a particular Request, and likely store some meta-data (has the user seen the Match yet? Have they rejected it?)
So, I'd probably generate a Match class like this:
rails g model Match seen_at:datetime deleted_at:datetime request:references product:references
And set up the associations as follows:
class Match < ApplicationRecord
belongs_to :request
belongs_to :product
end
class Request < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :request_categories
has_many :categories, through: :request_categories
has_many :matches
has_many :products, through: :matches
validates :user_id, presence: true
end
class Product < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :product_categories
has_many :categories, through: :product_categories
has_many :matches
has_many :requests, through: :matches
validates :user_id, presence: true
end
Also, you'll likely want to add the Request has_many :through to your Category model (I think you forgot that one):
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through: :product_categories
has_many :request_categories
has_many :requests, through: :request_categories
validates :name, presence: true, length: { minimum: 3, maximum: 25}
validates_uniqueness_of :name
end
The big part of the job is working out how to have your app periodically look for matches - you may want to start with the Active Job Basics documentation.
I'm wondering if there is a cleaner way to validate multiple relationships in rails. I don't mean validating an association, rather ensuring relationship integrity between two or more belongs_to associations. Here is some code as an example:
class User < ActiveRecord::Base
has_many :products, inverse_of: :user
has_many :purchases, inverse_of: :user
end
class Purchase < ActiveRecord::Base
belongs_to :user, inverse_of: :purchases
has_many :products, inverse_of: :purchase
end
class Product < ActiveRecord::Base
belongs_to :user, inverse_of: :products
belongs_to :purchase, inverse_of: :products
validates :user, :purchase, presence: true
validate :purchase_user
private
def purchase_user
errors.add(:purchase, 'purchase user does not match user') if user != purchase.user
end
end
The purchase_user validation method here checks that the user is the same as purchase.user and adds an error if they are not.
I could change it to use :inclusion like so:
validates :purchase, inclusion: { in: proc { |record| record.user.purchases } }
But that seems even more inefficient, any suggestions?
I just have this simple setup:
class Team < ActiveRecord::Base
has_many :players
has_many :users, -> { uniq }, through: :players
end
class User < ActiveRecord::Base
has_many :players
has_many :teams, -> { uniq }, through: :players
end
class Player < ActiveRecord::Base
belongs_to :team
belongs_to :user
validates :user_id, :uniqueness => { :scope => :team_id }
end
With this I can create multiple Teams with the same user combination by calling this twice:
Team.create(user_ids: ["1","2"])
How to make sure that there is not already another team with these users?
Given is a User Model who has_many Projects.
each Projects belongs_to a User
joined by a Assigned_projects Model
each Project has_many Expenses which belongs to a Project and a User
User Model:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
Project Model:
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
has_many :expenses
end
join Model:
class AssignedProject < ActiveRecord::Base
attr_accessible :project_id, :user_id, :position, :project
belongs_to :user, class_name: "User"
belongs_to :project, class_name: "Project"
validates :user_id, presence: true
validates :project_id, presence: true
end
Expenses Model:
class Expense < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
Sum all Expenses of a project is easy:
#current_project.expenses.sum(:amount)
I want to list the sum of all Expenses each User added.
The Question is how to sum all Expenses for each user who is assigned to the Project.
I'am looking for something like:
#current_user.#current_project.expenses.sum(:amount)
I assume you are trying to do the following. If not, please explain your question a bit more.
#current_project.expenses.where(user_id: #current_user.id).sum(:amount)