Validates uniqueness from foreign key of foreign key - ruby-on-rails

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

Related

What to write in this Note model to detect whether the current user is doctor or patient?

I have used devise gem in my project. I have a note model associated with user and appointment model.
class Note < ActiveRecord::Base
validates_presence_of :description, :message => "Please enter at least 5 characters"
belongs_to :appointment
belongs_to :user
before_create do
self.user_id = appointment.patient_id if (condition)
self.user_id = appointment.doctor_id if (condition)
end
end
What condition to write so as to determine whether I have created note as patient or doctor??
If you're using enum to work with your roles, then you can query them by name, that's to say user.patient? would return true if such user is a patient, same case for doctor.
So you could add that to your before_create callback:
before_create do
self.user_id = appointment.patient_id if user.patient?
...
end
Maybe you could get the role of the user and use it to get the appointment id dynamically, like:
before_create do
self.user_id = appointment.public_send("#{self.user.role}_id")
end
This way you avoid repetition on having to specify one by one each role.

Rails Validation Error on existing data

I am building a simple expenses management app on rails 5.1.4. I am using the following five models.
Payees
class Payee < ApplicationRecord
has_many :expenses
validates :title, uniqueness: true, presence: true
end
Accounts
class Account < ApplicationRecord
before_save :update_balance
validates :balance, numericality: { greater_than_or_equal_to: 0 }
has_many :expenses
end
Budgets
class Budget < ApplicationRecord
belongs_to :categories
has_many :expenses, through: :categories
end
Categories
class Category < ApplicationRecord
validates :title, uniqueness: true, presence: true
has_many :expenses
has_one :budget
end
Expenses
class Expense < ApplicationRecord
belongs_to :categories
belongs_to :budgets
belongs_to :payees
belongs_to :accounts
validates :title, :value, presence: true
before_save :default_account
end
When I try to create a new expense I am facing a validation error
Validation failed: Categories must exist, Budgets must exist, Payees must exist, Accounts must exist
The issue is that all the above records exist. To explain my self let's say I am passing the params account_id: 1, payee_id: 1, category_id: 1. If I do:
Account.find(1) #=> Finds the record
Category.find(1) #=> also ok
Payee.find(1) #=> also ok
I am aware of the solution referred in this question (adding optional: true) but I don't get why I should do that while all of the above exist
Edit
The code that is raising the error is:
def create
#expense = Expense.create!(title: params[:expense]['title'],
value: params[:expense]['value'],
date: params[:expense]['date'],
comment: params[:expense]['comment'],
payee_id: params[:expense]['payee_id'],
category_id: params[:expense]['category_id'],
account_id: params[:expense]['account_id'])
end
The parameters that are passed through the form are
{"utf8"=>"✓",
"authenticity_token"=>"DWd1HEcBC3DhUahfOQcdaY0/oE+VHapxxE+HPUb0I6iSiqMxkz6l+vlK+1zhb66HnZ/vZRUVG4ojTdWUCjHtGg==",
"expense"=>{"title"=>"test", "value"=>"-20", "category_id"=>"1", "payee_id"=>"2", "date"=>"2018-01-21", "account_id"=>"1", "comment"=>""},
"commit"=>"Submit"}
I would first start by commenting out all your model validations, then creating an expense. Add back one model validation at a time, each time test creating an expense to see what validation is causing the error.
also you may want to change how you're creating the expense to something like below.
change your controllers create action to
def create
#expense = Expense.new(expense_params)
if #expense.save
flash[:success] = "expense created"
redirect_to expense_url(#expense.id)
else
render 'new'
end
end
next under your private method at the bottom of your controller you want to do something like this
private
# Never trust parameters from the scary internet, only allow the white list through.
def expense_params
params.require(:expense).permit(:title, :value, :date, etc...)
end
I finally found out where the problem is! It was the naming of the classes/models that raised the error. I had named my models on singular (Account, Category, etc) while all references are searching for plurals ( Accounts, Categories, etc). I had to re-do all migrations from the very beginning in order to make it work the proper way!
Thanks to everyone for spending the time though!

Saving Rails associations after creating User

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

Validate that belongs_to is in allowed values

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

How to validate that a dependent row belongs to a user

I have a model called Category;
class Category < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :name, presence: true
validates :user_id, presence: true
end
And, I have a model called Group:
class Group < ActiveRecord::Base
belongs_to :user
has_many :categories
validates :name, presence: true
validates :user_id, presence: true
end
As you can see, a group can have many categories. When a user adds a category or updates it's group_id value, I want to check that the group belongs to that user. I don't want users adding to and update categories to another user's group. What is best practise to validate this prior to saving? Thanks
validate :ownership_of_group
def ownership_of_group
ids = []
ids << self.group_id
ids << self.group_id_was if self.group_id_was.present?
if(Group.find(ids).reject {|group| group.user_id == self.user_id}.present?)
# if all of them is owned by the user the array will return an empty array
errors.add(:user_id, 'You cant edit a category that is not yours')
end
end
If we say group_id we get the current value that is being set by the use.
If we say group_id_was it get the old value before the update.
In the update we need to handle both in the create we have no previous value.

Resources