Rails validate that related field has a related field - ruby-on-rails

I have these four models:
class Workgroup < ActiveRecord::Base
has_many :empgroups
has_many :employees, through: :empgroups
has_many :workorders
class Empgroup < ActiveRecord::Base
attr_accessible :employee_id, :workgroup_id
belongs_to :employee
belongs_to :workgroup
class Employee < ActiveRecord::Base
has_many :empgroups
has_many :workgroups, through: :empgroups
has_many :workorders
class Workorder < ActiveRecord::Base
belongs_to :employee
belongs_to :workgroup
When a new workorder is created, I want to validate that the employee belongs to the selected workgroup.
How can I code that validation in the workorder model?
Thanks for the help!

I would write a custom validation method in the workorder model. So it might look something like this.
class Workorder < ActiveRecord::Base
belongs_to :employee
belongs_to :workgroup
validate :check_employee_workgroup
private
def check_employee_workgroup
errors.add(:employee_id, "Employee is not available in this work group") unless
self.employee.workgroups.select(:id).where(id: [self.workgroup_id]).exists?
end
end

The need to do this may be a design issue. Perhaps you should revise your classes to look something like this:
class Workgroup < ActiveRecord::Base
has_many :empgroups
has_many :employees, through: :empgroups
class Empgroup < ActiveRecord::Base
attr_accessible :employee_id, :workgroup_id
belongs_to :employee
belongs_to :workgroup
class Employee < ActiveRecord::Base
has_many :empgroups
has_many :workgroups, through: :empgroups
class Workorder < ActiveRecord::Base
belongs_to :empgroup
This way, you don't need a custom validator at all. From there, you can add in any other through: relationships necessary

Related

Rails has_one association through mapping table

Given these 4 Rails models:
class Apple < ActiveRecord::Base
has_one: ?
end
class Banana < ActiveRecord::Base
has_one: ?
end
class FruitMapping < ActiveRecord::Base
belongs_to :fruit, polymorphic: true
has_one :cart
end
class Cart < ActiveRecord::Base
end
How can I connect the has_one of the Apple/Banana to Cart, so that when I write apple.cart I will get the relevant Cart (through the mappings table)?
class Apple < ActiveRecord::Base
has_one :fruit_mapping, as: :fruit
end
class Cart < ActiveRecord::Base
has_many :fruit_mappigns
has_many :apples, through: :fruit_mappings, source: :fruit, source_type: 'Apple'
has_many :bananas, through: :fruit_mappings, source: :fruit, source_type: 'Banana'
end
Using the source and source_type options, you can define the polymorphic relationships. If using source and source_type are depricated in the Rails version you're using you can try
has_many :apples, through: :fruit_mappings, class_name: 'Apple', foreign_key: :fruit_id

Find records that have two exact instances of a many-to-many relation

I have an application with a business logic concerning products with multiple variants:
class Task < ApplicationRecord
belongs_to :variant
end
class Variant < ApplicationRecord
belongs_to :product
has_many :variant_option_values
has_many :option_values, through: :variant_option_values
has_many :prices
end
class Product < ApplicationRecord
has_many :product_option_types
has_many :option_types, through: :product_option_types
has_many :variants
end
class OptionValue < ApplicationRecord
belongs_to :option_type
end
class OptionType < ApplicationRecord
has_many :product_option_types
has_many :products, through: :product_option_types
has_many :option_values
end
class ProductOptionType < ApplicationRecord
belongs_to :product
belongs_to :option_type
end
class VariantOptionValue < ApplicationRecord
belongs_to :variant
belongs_to :option_value
end
The ERD looks like this:
Having a product product_1 how can I find its variants that have OptionValue instances option_value_1, option_value_2 and option_value_3? Note that the variant has to have all three option values at the same time and can have more than those three (but not necessarily).
option_values = [option_value_1, option_value_2, option_value_3]
Variant.include(product: [option_types: :option_values])
.where("option_values.id IN (?)", option_values.map(&:id))
.group("products.id")
.having("count(*) >= ?", option_values.size)

How to correctly set up model associations

I want a user to be able to create and save or choose an existing client to add to an invoice. There can only be one client per invoice.
I currently have 3 models users invoices and items
I am using a simple has_many relationship currently but I am getting confused now that I want to add a new table clients. I was hoping I could get some advice on what association to use.
My current associations
class User < ActiveRecord::Base
has_many :invoices
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items, :dependent => :destroy
class Item < ActiveRecord::Base
belongs_to :invoice
So I was thinking to do something simple like adding has_many :clients
to users, add has_one :client to invoices and add the table clients
class User < ActiveRecord::Base
has_many :invoices
has_many :clients
class Client < ActiveRecord::Base
belongs_to : user
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items, :dependent => :destroy
has_one :client
Would this work? Is there a better way?
It is very rare that you will use has_one. In your case the following models make more sense:
class User < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :user
belongs_to :client
has_many :items, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :invoice
end
class Client < ActiveRecord::Base
has_many :invoices
has_many :items, through: :invoices
end

Struggling with has_many :through

Suppose I have 3 Models like this (not sure if this is correct):
class User < ActiveRecord::Base
has_many :lessons
has_many :points, through: :progress
end
class Progress < ActiveRecord::Base
belongs_to :user
has_many :lessons
end
class Lesson < ActiveRecord::Base
belongs_to :progress
end
(The Progress table has user_id and lesson_id fields.)
How would I make it so calling #user.points would return the amount of entries into the Progress table. Also, how would I build a relationship?
class User < ActiveRecord::Base
has_many :progresses
has_many :lessons, through: :progresses
end
class Progress < ActiveRecord::Base
belongs_to :user
belongs_to :lesson
end
class Lesson < ActiveRecord::Base
has_many :progresses
end
First, you need to set up the association for progress on your User model, so that the through association will work:
class User < ActiveRecord::Base
has_many :lessons
has_many :progress
has_many :points, through: :progress
end
Then you'll need to define a method (or relation) of points on your Progress table. Or, if you simply want a count of records, you could do: #user.points.size

Association for membership join model in Rails

I have the following models
Class User
has_many :memberships
Class Membership
belongs_to :user
The membership table has two columns, user_id and organization_id.
However the organization_id is also a user id.
How can I specificy an association so that:
#user.organizations returns a list of organizations(other users) of which this user is a member.
I think the correct way of doing this would be to create three models as follows:
#user.rb
class User < ActiveRecord::Base
has_many :memberships
has_one :organization
end
#organization.rb
class Organization < ActiveRecord::Base
attr_accessible :user_id
belongs_to :user
has_many :memberships
end
#membership.rb
class Membership < ActiveRecord::Base
attr_accessible :user_id, :organization_id
belongs_to :user
belongs_to :organization
end
Figured it out:
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :organization, class_name: "User"
end
class User < ActiveRecord::Base
has_many :memberships
has_many :organizations, through: :memberships
end

Resources