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?
-
\$\begingroup\$ Your comment makes sense. How can I achieve it? \$\endgroup\$LearningROR– LearningROR2017年02月19日 18:30:55 +00:00Commented Feb 19, 2017 at 18:30
-
\$\begingroup\$ Converted my comment to an answer with some more detail \$\endgroup\$Flambino– Flambino2017年02月19日 19:25:06 +00:00Commented Feb 19, 2017 at 19:25
2 Answers 2
(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