2
\$\begingroup\$

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?

\$\endgroup\$
2
  • \$\begingroup\$ Your comment makes sense. How can I achieve it? \$\endgroup\$ Commented Feb 19, 2017 at 18:30
  • \$\begingroup\$ Converted my comment to an answer with some more detail \$\endgroup\$
    – Flambino
    Commented Feb 19, 2017 at 19:25

2 Answers 2

1
\$\begingroup\$

(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.

\$\endgroup\$
1
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.