Home > Enterprise >  Generating a has_one association for an existing model correctly
Generating a has_one association for an existing model correctly

Time:01-06

I'm experiencing some issues when trying to add a has_one relation to existing model. I ran rails g migration AddPracticeToLesson practice:references and generated the following migration

class AddPracticeToLesson < ActiveRecord::Migration[7.0]
  def change
    add_reference :lessons, :practice, null: true, foreign_key: true
  end
end

and I have the lesson model as

class Lesson < ApplicationRecord
  extend FriendlyId
  friendly_id :title, use: :slugged

  acts_as_list

  belongs_to :course

  has_many :sections, -> { order(position: :asc) }, as: :sectionable, dependent: :destroy
  has_one :quiz, through: :sections
  has_one :practice # ADDED THIS
  has_one_attached :cover_image
end

to which I added the line has_one :practice. I also added belongs_to :lesson, optional: true for the practice model which now looks like

class Practice < ApplicationRecord
  extend FriendlyId
  friendly_id :title, use: :slugged

  enum status: [:unpublished, :published]

  has_many :sections, -> { order(position: :asc) }, as: :sectionable, dependent: :destroy
  has_one :quiz, through: :sections
  has_one_attached :cover_image

  belongs_to :lesson, optional: true
end

but now upon running @lesson.practice will result in an error stating

PG::UndefinedColumn: ERROR: column practices.lesson_id does not exist

which I don't understand. Why is it trying to access practices.lesson_id and not just find the practice with the lesson.practice_id?

CodePudding user response:

The problem is most likely that you have defined the reference on the wrong table.

From https://guides.rubyonrails.org/v7.0/association_basics.html#the-has-one-association:

A has_one association indicates that one other model has a reference to this model. That model can be fetched through this association.

What this means in practice is that your table practice (or practices whichever is the real table name) should have a reference to lessons.

In your migration, change

add_reference :lessons, :practice, null: true, foreign_key: true

to

add_reference :practice, :lessons, null: true, foreign_key: true

If you want to use the Rails generators,

rails g migration AddLessonRefToPractices lesson:references

seemed to create the correct migration

class AddLessonRefToPractices < ActiveRecord::Migration[7.0]
  def change
    add_reference :practices, :lesson, foreign_key: true
  end
end

I took the naming convention for the migration from https://guides.rubyonrails.org/v7.0/active_record_migrations.html#creating-a-standalone-migration (scroll down to where they define the AddUserRefToProducts migration, almost until section 2.2).

Naming the migration matters, at least in some cases, as Rails tries to conclude from the name and given attributes what you want to do.

Put another way, whenever you use the association belongs_to the reference field must always be defined in that table (https://guides.rubyonrails.org/v7.0/association_basics.html#the-belongs-to-association). As your Practice model defines belongs_to :lesson, the reference field must be defined in the table for Practice.

  • Related