简体   繁体   中英

The has_many :through Association

Example from Guide with additional property in association model

class CreateAppointments < ActiveRecord::Migration
  def change
    create_table :physicians do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :patients do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :appointments do |t|
      t.belongs_to :physician, index: true
      t.belongs_to :patient, index: true
      t.datetime :appointment_date
      t.timestamps null: false
    end
  end
end

Model Appointment has a validation:

class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient

  validates :appointment_date, presence: true
end

class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, through: :appointments
end

When I add patient to physicians some_physician.patient << some_patient I have to define appointment_date . How to do it correctly?

How to do it correctly?

In my opinion, you can define a method, and pass that method into before_save callback.

class Appointment < ActiveRecord::Base
  before_save :set_appointment_date

  private
  def set_appointment_date
    self.appointment_date = Time.now
  end
end

That's how you don't need to explicitly set the appointment_date each time you create a relationship. It will be automatically set for you.

Edit: before_save will also fire if you later want to update your appointment object, though that is a very rare case. But it will update the appointment_date . For that, you can use before_create callback, and appointment_date will be touched only once in that case.

The reason you have to define it is because some_physician.patient << some_patient creates an implicit appointment to link the two. An appointment is invalid if you leave appointment_date blank.

If you don't want an appointment, define a new relationship between physicians and patients .

Otherwise it makes sense to do something like:

appointment = some_physician.appointments.create(patient: some_patient, appointment_date: Time.now)

It is can be achieved without before_save callback, Rails update timestamps automatically. Just create a right migration file, assume you are use postgresql as orm.

create_table :appointments do |t|
  t.belongs_to :physician, index: true
  t.belongs_to :patient, index: true
  t.timestamps :appointment_date, default: 'now()'
  t.timestamps null: false
end

Instead datetime columntype use timestamps as created_at or updated_at with a plain sql function now()

Current date and time (equivalent to current_timestamp)

It's happens under the hood.

Some references

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM