Reputation: 851
I have two ActiveRecord models like so:
class User < ActiveRecord::Base
has_one :contact_information
end
class ContactInformation < ActiveRecord::Base
belongs_to :user
end
This is my setup for a One-to-One relationship between the user and contact information table.
The issue I am encountering is when I create a new contact_information entry that points to a user that already has a existing contact_information, the new record is created including the relationship, meaning I have two contact_information records pointing to the same user, even though it is a one-to-one relationship.
Rails seems to pick the first record and returns it.
For example in irb:
> User.create
> ContactInformation.create(user: User.last)
> ContactInformation.create(user: User.last)
> ContactInformation.all
=> #<ActiveRecord::Relation [#<ContactInformation id: 3, created_at: "2016-01-04 22:28:11", updated_at: "2016-01-04 22:28:11", user_id: 2>, #<ContactInformation id: 4, created_at: "2016-01-04 22:28:18", updated_at: "2016-01-04 22:28:18", user_id: 2>]>
Both with user_id set to 2.
The ideal scenario would be a validation failure from contact_information when a record is created for a user that already has a contact_information.
I came up with this initial custom validation method for the Contact Information model that kind of works for creation (Need to make one for update)
validate :check_parent, on: :create
def check_parent
if user.contact_information.id.nil?
errors.add(:user, "already has contact information")
end
end
I am wondering if there is a more rails-y way of solving this problem? Or will I need to create a custom validator like this for all has_one relationships?
Thanks
Upvotes: 0
Views: 180
Reputation: 1734
You will need to have a validation but you don't need a custom one:
class ContactInformation < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :user_id
end
You may also want to add a unique index in the database for that column. It would not only make the query performant but will also protect your app against race conditions in case you are running multiple instances of it in parallel.
Upvotes: 1
Reputation: 2378
A few options are available to you.
You could use validates_uniqueness_of on your ContactInformation
model:
class ContactInformation < ActiveRecord::Base
validates_uniqueness_of :user_id
end
However, this will query the database to ensure that no duplicates exist on every save and could be very slow.
Another option is to have your database handle this for you. Depending on what you're using ActiveRecord
with, adding a uniqueness constraint on the user_id
column of your contact_informations
table could be valuable.
I would suggest not using validates_uniqueness_of
if your dataset is large, otherwise it might be perfect.
Make sure you at least add an index to the user_id
column on that table:
class AddIndexOnContactInformationToUserId < ActiveRecord::Migration
add_index :contact_information, :user_id
end
Upvotes: 1