1

I have the following

class User < ApplicationRecord

  has_one :physician

end

and

class Physician < ApplicationRecord

  belongs_to :user

end

Since a user can only have one physician, I was surprised that multiple physician records can be created with the same user id.

Why is this? And how to I prevent it?

stevec
  • 41,291
  • 27
  • 223
  • 311
  • Yeah, rails doesn't update one the record, it just uses the last one. This answer uses a db level constraint... https://stackoverflow.com/questions/43783347/how-to-limit-has-one-association-to-a-single-record – dbugger Aug 29 '20 at 19:56
  • How are you creating physicians ? – Maxence Aug 29 '20 at 19:58
  • @Maxence the idea is that every single user who signs up is automatically considered a Patient, and a small subset of those patients will be doctors in real life, and they can become a Physician. Suppose I am a doctor in real life and I sign up, then I become a User and a Patient, and once I apply inside the app, I become a Physician too. But the user "steve" can certainly only be *one* physician. I hope this makes sense? – stevec Aug 30 '20 at 04:22

2 Answers2

1

A belongs_to association does not in any way guarantee that the values are unique. It only stipulates that the association is stored in a foreign key on this models table and thus can only have a single value.

has_one doesn't actually provide any guarantees either. It just specifies that this table is referred to on the other table. If there are multiple matching rows on the other table it will chose the last.

If you want to enforce uniqueness per table you need a validation:

class Physician < ApplicationRecord
  belongs_to :user
  validates_uniqueness_of :user_id
end

And a database index to actually guarantee uniqueness on the database level:

class AddUniqueIndexToPhysicians < ActiveRecord::Migration[6.0]
  def change
    add_index :physicians, :user_id, unique: true
  end
end
max
  • 96,212
  • 14
  • 104
  • 165
  • Thanks Max! Random question. Why is the migration necessary? (I would have guessed the validation would have been enough)? – stevec Sep 03 '20 at 15:16
  • 1
    Because of race conditions. The linked article explains it far better than I can. – max Sep 03 '20 at 15:19
  • Took me a while, but just read the article. Great explanation – stevec Sep 05 '20 at 13:53
  • For some reason `ArgumentError: Index name 'index_physicians_on_user_id' on table 'physicians' already exists`. Perhaps `validates_uniqueness_of :user_id` alone is enough? – stevec Sep 09 '20 at 20:11
0

Physician probably shouldn't have a user_id associated to it:

class User
  belongs_to :physician # has a physician_id column
end

class Physician
  has_many :users # has no mention of user_id in its schema
end

belongs_to is required by default for Rails 5 and later. If users can onboard without a physician and you need to disable this:

class User
  belongs_to :physician, optional: true 
end 

And then later, validate the presence of physician only after some condition.

Josh Brody
  • 5,153
  • 1
  • 14
  • 25
  • Thanks for the help! The idea is that every user who signs up will be a user, and some (who are doctors in real life) can apply to become a Physician. So 99% of users will not be a physician, but ~1% will be, and they'll also have a user account. E.g. Suppose I'm a doctor in real life and I sign up, I'll have a user account then apply to be physician and get a Physician record. Presently the database doesn't stop me making multiple Physician records (but it should, and that's the part I'm having trouble with) – stevec Aug 30 '20 at 04:49