I have this structure
class Organization
has_many :clients
end
class Client
belongs_to :organization
has_many :contacts
end
class Contact
belongs_to :client
belongs_to :organization
end
How can I make sure that when client is assigned to a contact he is a child of a specific organization and not allow a client from another organization to be assigned ?
While searching I did find a scope parameter can be added but that seems not to be evaluated when client_id is assigned.
Update
Here is an example from Rails Docs :
validates :name, uniqueness: { scope: :year,message: "should happen once per year" }
I'm looking for something like "if client is set it must be in Organization.clients"
class Contact
#...
validate :client_organization
def client_organization
unless client.nil?
unless organization == client.organization
errors.add(:organization, "can't be different for client.")
end
end
end
end
Related
I have 3 models in my rails application, User, Course, and CourseTemplate.
A Course belongs to a User and a CourseTemplate belongs to a Course.
What I want to do is to validate the uniqueness between the CourseTemplate name and the User id.
Is this possible?
Without denormalization of data
class CourseTemplate < ActiveRecord::Base
belongs_to :course
has_one :user, through: :course
validate :unique_course_template_for_user
private
def unique_course_template_for_user
errors.add(:name, 'Must be unique') if CourseTemplate.find_by(user: user.id, name: self.name).count > 0
end
end
With denormalization of data
If you're ok with some denormalization of your data, you could add user_id to CourseTemplate, and then simply use the scope feature of validates uniqueness.
Below I show how to use callbacks to maintain the user_id in the CourseTemplate. Note that it assumes a course cannot be moved to a different user.
class CourseTemplate < ActiveRecord::Base
before_create :copy_user_id_from_course
validates :name, uniqueness: { scope: :user_id, message: 'Must be unique for the same user'}
private
def copy_user_id_from_course
self.user_id = course.user_id
true
end
end
If the course can be moved to a different user, you should add a callback on Course:
class Course < ActiveRecord::Base
after_save :set_course_templates_user, if: :user_id_changed?
private
def set_course_templates_user
course_templates.update_all user_id: self.user_id
end
end
There are 2 tables. One is User(id, name, email) and the other is Student(id, who_id).
I wanna Use this way:
Student.find(id).name, Student.find(id).email
rather than:
User.find(student.who_id).name, User.find(student.who_id).email
to get data.
How should I do?
btw, I cannot change who_id to user_id for any reason.
class User < ActiveRecord::Base
end
class Student < ActiveRecord::Base
end
You can add name and email methods in your Student model, like so:
class Student < ActiveRecord::Base
belongs_to :user, class_name: :User, foreign_key: 'who_id'
def name
user.name
end
def email
user.email
end
end
You could also use Rail's delegate method to do the same thing in less code:
class Student < ActiveRecord::Base
belongs_to :user, class_name: :User, foreign_key: 'who_id'
delegate :name, to: :user
delegate :email, to: :user
end
And once you ge tthat working, rather than Student.find(id).name, Student.find(id).email (which will fetch the data from the database twice) you should instead do this:
student = Student.find(id) #single call to the database
# get the properties from the previous database call
student.name
student.email
I'm new to Rails and ActiveRecord and need some help. Basically, I have 4 models: User, Property, PropertyAccount, and AccountInvitation. Users and Properties have a many to many relationship via PropertyAccounts. AccountInvitations have a user's email and a property_id.
What I want to happen is that after a user registers on my app, his user account is automatically associated with some pre-created Properties. What I don't know how to do is write the query to get the Property objects from the AccountInvitations and save them to the User object. Please see def assign_properties for my pseudo code. Any help is welcome, thanks so much!
class User < ActiveRecord::Base
has_many :property_accounts
has_many :properties, through: :property_accounts
after_create :assign_properties
# Check to see if user has any pre-assigned properties, and if so assign them
def assign_properties
account_invitations = AccountInvitations.where(email: self.email)
if account_invitations.any?
account_invitations.each do |i|
properties += Property.find(i.property_id)
end
self.properties = properties
self.save
end
end
end
class AccountInvitation < ActiveRecord::Base
belongs_to :property
validates :property_id, presence: true
validates :email, uniqueness: {scope: :property_id}
end
class Property < ActiveRecord::Base
has_many :account_invitations
has_many :property_accounts
has_many :users, through: :property_accounts
end
class PropertyAccount < ActiveRecord::Base
belongs_to :property
belongs_to :user
end
Thanks to #wangthony , I looked at the includes method on http://apidock.com/rails/ActiveRecord/QueryMethods/includes and tweaked one of their examples in order to get this to work. Here's the solution:
def assign_property
self.properties = Property.includes(:account_invitations).where('account_invitations.email = ?', self.email).references(:account_invitations)
self.save
end
I believe you can do this:
user.properties = Property.includes(:account_invitations).where(email: user.email)
user.save
class Employee < ActiveRecord::Base
has_one :office
end
class Office < ActiveRecord::Base
belongs_to :employee
end
There is a default Office that all employees are in and some will be in a different office. Is it better to make the foreign_key null for everyone and just contain a value for the special people or create an office and make that the default for everyone except the special people?
I recommend you to change your relations to the following:
class Employee < ActiveRecord::Base
belongs_to :office
validates :office_id, presence: true # this forces the Employee objects to have a value for `office_id`
end
class Office < ActiveRecord::Base
has_many :employees
end
With this configuration, you could make requests like this:
montreal_employees = Office.where(location: 'Montreal').employees
To make a default value, you could add a class method on the Office model in order to use the default office if none is set for a new Employee:
# Office model
def self.default
self.where(internal_reference: :default).first # this line implies that you have a column internal_reference
# and that there is a record having it set to 'default'
end
# Employee model
before_create :set_office_to_default, if: ->{ self.office_id.blank? }
def set_office_to_default
self.office = Office.default
end
A Contact has a User assigned to them:
class Contact < ActiveRecord::Base
...
belongs_to :user
...
end
The user model has a field I want to exclude any time a user object or objects are returned from db. One of the ways to make it work is to add a default scope:
class User < ActiveRecord::Base
...
has_many :contacts
...
default_scope select((column_names - ['encrypted_password']).map { |column_name| "`#{table_name}`.`#{column_name}`"})
end
So in console if I do:
User.first
The select statement and result set do not include 'encrypted_password'.
However, if I do:
c = Contact.includes(:user).first
c.user
they do. The default scope on the User model does not get applied in this case and the 'encrypted_password' field is shown.
So my question is why? And also, is there a clean way to specify what fields should be returned on related object(s)?
You should just be able to use the :select option on the belongs_to relationship. Something like this:
class Contact < ActiveRecord::Base
...
belongs_to :user, :select => [:id, :first_name, :last_name, :email]
...
end