Fake a composite primary key? Rails

后端 未结 3 1431
刺人心
刺人心 2021-02-09 15:59

I have a table with id|patient_id|client_id|active. A record is unique by patient_id, client_id meaning there should only be one enrollment per patient per client. Normally I wo

3条回答
  •  死守一世寂寞
    2021-02-09 16:45

    Sounds like you have a model relationship of:

    class Client < ActiveRecord::Base
      has_many :patients, :through => :enrollments
      has_many :enrollments
    end
    
    class ClientPatient < ActiveRecord::Base
      belongs_to :client
      belongs_to :patient
    end
    
    class Patient < ActiveRecord::Base
      has_many :clients, :through => :enrollments
      has_many :enrollments
    end
    

    To enforce your constraint I would do it in ActiveRecord, so that you get proper feedback when attempting to save a record that breaks the constraint. I would just modify your ClientPatient model like so:

    class Enrollment < ActiveRecord::Base
      belongs_to :client
      belongs_to :patient
      validates_uniqueness_of :patient_id, :scope => :client_id
    end
    

    Be careful though because, while this is great for small-scale applications it is still prone to possible race conditions as described here: http://apidock.com/rails/v3.0.5/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of under "Concurrency and Integrity"

    As they describe there, you should also add a unique index to the table in the database. This will provide two immediate benefits:

    • The validation check and any searches through this model based on these two id's will perform faster (since they're indexed)
    • The uniqueness constraint will be enforced DB-side, and on the rare occurrence of a race condition you won't get bad data saved to the database... although users will get a 500 Server Error if you don't catch the error.

    In a migration file add the following:

    add_index :enrollments, [:patient_id, :client_id], :unique => true
    

    Hopefully this was helpful :)

    Edit (fixed some naming issues and a couple obvious bugs):

    It's then very easy to find the data you're looking for:

    Client.find_by_name("Bob Smith").patients
    Patient.find_by_name("Henry Person").clients
    

提交回复
热议问题