Search code examples
ruby-on-railsforeign-keysvalidates-uniqueness-of

Validates uniqueness from foreign key of foreign key


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?


Solution

  • 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