Hilmi Yamcı
Hilmi Yamcı

Reputation: 473

Ruby on Rails Active associations clone and dup methods

In my ruby on rails application I have a problem while I'm trying to save a model after cloning it. I have the following models.

class Company < ApplicationRecord

    has_many :employees

end

class Employee < ApplicationRecord

    belongs_to :company
    has_one :user   

end

class User < ApplicationRecord

    belongs_to :employee

end

When I user the following piece of code I get 'ActiveRecord::RecordInvalid: Validation failed: Employee must exist' error.

company = Company.new
employee = Employee.new(company: company)
user = User.new(name: 'John',email:   '[email protected]',password: 'password')
user.employee = employee

u = user.dup

u.save!

On the other hand, when I use 'clone' instead of 'dup' Rails tries to save User model twice and this leads exception

company = Company.new
employee = Employee.new(company: company)
user = User.new(name: 'John',email:   '[email protected]',password: 'password')
user.employee = employee

u = user.clone

u.save!  

If I save model without dupping and cloning, there is no problem. In my application I'm using builder pattern and have to use one of the methods of dup or clone.

I can't see what I'm missing.

Any suggestions ?

Thanks.

Upvotes: 1

Views: 1405

Answers (1)

Adamantish
Adamantish

Reputation: 1999

In both cases the trouble is the association you're making before duplicating. This is one of those things that's hard to do because there are better ways to approach it in the real world.

In general duplicating things gets sticky when they're attached to other things. It's not clear from a command like clone which of the attached things you also want to duplicate and in which way and what about the things attached to those? That's why you'll have to write more explicit code.

As this answer explains, dup doesn't copy associations. With clone both your original user and the clone are attached to the same employee. I can't imagine this would be your intention if making something like this for real and indeed it confuses active record into cascading the save back to the original object.

It's more likely that you want to make a new employee for each duplicated user so handle that step after duplication not before.

However, if all users really should belong to the same employee (or you needed to make an association with some more credible template value like the company) then you can create that database record up-front then refer to it explicitly as a record rather than as a ruby object.

 company = Company.new
 employee = Employee.create!(company: company)
 user = User.new(name: 'John', 
                 email: '[email protected]',
                 password: 'password', 
                 employee_id: employee.id)

 u = user.dup

 u.save!

Upvotes: 1

Related Questions