# Update single common column in multiple has-many relationships rails

I want to update a single column in 4 tables. All relationships are has-many among them.

• Private schools has many private classes
• Private classes has many lesson plans
• Lesson plans has many quizzes
• Lesson plans has many documents

The code I am working with is:

@private_school.private_classes.each do |private_class|
private_class.lesson_plans.each do |lesson_plan|
lesson_plan.update_attribute("price_type", @private_school.price_type)
lesson_plan.quizzes.update_attribute("price_type", @private_school.price_type)
lesson_plan.documents.update_attribute("price_type", @private_school.price_type)
end
end


Is this a best approach to follow or is there any more option to tackle this? What are best practices about this scenario?

• Your comment makes sense. How can I achieve it? – LearningROR Feb 19 '17 at 18:30
• Converted my comment to an answer with some more detail – Flambino Feb 19 '17 at 19:25

(Converted a comment into this answer)

If all the records should use @private_school.price_type as their own price_type, then make them fetch it from the school they belong to - don't duplicate the same value across a bunch of tables when you already have it in one place. Keep the database normalized.

Simplest way to do this is to add a price_type method to each of the non-school models. The method simply fetches and returns <belongs_to relation>.price_type. I.e. Document#price_type would call its owner's method, which would be LessonPlan#price_type, which would call it's owner, etc.. until it hits the school's price_type attribute.

It's not the most efficient as it may need to load some records in the chain, but it's a start.

since you have to cache price_type on every table, you could use callbacks to not have to do this in a... controller action? or wherever. This could be a first step to simplifying.

class Quiz < ApplicationRecord
belongs_to :lesson_plan

before_save :set_price_type
def set_price_type
price_type = lesson_plan.price_type
end
end

class Document < ApplicationRecord
belongs_to :lesson_plan
before_create :set_price_type

before_save :set_price_type
def set_price_type
price_type = lesson_plan.price_type
end
end

class LessonPlan < ApplicationRecord
belongs_to :private_class
has_many :documents
has_many :quizzes

before_save :set_price_type
after_save :update_relationships

def set_price_type
price_type = private_class.price_type
end

def update_relationships
quizzes.update_all(:price_type, price_type)
document.update_all(:price_type, price_type)
end
end

class PrivateClass < ApplicationRecord
belongs_to :private_school
has_many :lesson_plans

after_save :update_relationships
before_save :set_price_type

def set_price_type
price_type = private_school.price_type
end

def update_relationships
lesson_plans.update_all(:price_type, price_type)
document.update_all(:price_type, price_type)
end
end

class PrivateSchool < ApplicationRecord
has_many :private_classes
after_save :update_private_classes

def update_private_classes
private_classes.update_all(:price_type, price_type)
end
end


then you don't have to do anything extra to keep price type up to date.

The much larger and tougher question to answer is why is this variable being cached on each table in the first place? This is going to eventually make maintenance a nightmare. You need to take a hard look at where you are accessing this value and ask yourself if you really need to cache it.

These decisions are made usually in response to slow queries in views or controller actions, but most of the time that slowness can be resolved in better ways. In general, favor query optimization over denormalization