I have two models with a 1 to 1 reciprocal dependency.
User (1..1) <-> (1..1) Address
I have set up it like this:
class User
has_one :address
validates :address, presence: true
# automatic create address object for new Users.
before_validation :create_address, on: :create
def create_address
if !address.present?
build_address
end
end
end
class Address
belongs_to :user
validates :user, presence: true
end
I also have these factories (FactoryBot)
factory :user do
end
factory :address do
association :user
end
But this generates infinite problems.
When I try to create an Address
using FactoryBot.create(:address)
it creates 2 Addresses for the same User.
The only solution I found is either to remove the automatic creation of Address in the User class, or remove the automatic creation of the User in the Address factory.
In any case, the factories will be incomplete and will fail if I don't expecitly add the missing dependency.
I can also remove the validations but this sounds even uglier to me.
I have tried many combinations. But it is impossible to save one of the instances without the other being already saved, and none of them can be saved without the other :/
What would be your approach to this?
You can use inline association definition and use instance
method that refers to the model being created:
factory :user
factory :address do
user { association :user, address: instance }
end
>> FactoryBot.create(:address)
TRANSACTION (0.1ms) begin transaction
User Create (0.5ms) INSERT INTO "users" ...
Address Create (0.1ms) INSERT INTO "addresses" ...
TRANSACTION (0.1ms) commit transaction
=> #<Address:0x00007fcb5a00ce1 ...>
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#interconnected-associations
class Address
belongs_to :user
# ^ belongs to is already required. extra validation is redundant.
validates :user, presence: true
end
>> Address.validators
=>
# validation from belongs to vvv
[#<ActiveRecord::Validations::PresenceValidator:0x00007fb9d6853878 @attributes=[:user], @options={:message=>:required}>,
#<ActiveRecord::Validations::PresenceValidator:0x00007fb9d687b238 @attributes=[:user], @options={}>]
# your presence validation ^^^