We've got a Devise User model that we need to create and associate with an organization upon sign up.
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
acts_as_tenant(:tenant)
acts_as_paranoid
belongs_to :organization, :autosave => true
before_validation :create_organization
def create_organization
org = Organization.new(name: self.email)
org.uuid = UUIDTools::UUID.random_create.to_s
if org.save
self.organization_id = org.uuid
end
end
end
The problem we're having is that if something fails on the User form (mismatched passwords, etc) we are left with an orphaned Organization. On the flip side, if we wrap the create organization code in a
Warden::Manager.after_authentication do |user,auth,opts|
make org
end
Then the Organization must exist error gets placed in the errors array and printed to the screen, which we also don't want because Users don't really need to know about the Organization behind the scenes.
How do you set the Organization association so that the User becomes valid but not save the Users Organization association until you know everything else is valid as well?
Replace create
with build
, and don't persist the record until the parent record is valid. A has_one :foo
association defines a build_foo
method for you, which is what you should use to build your organization. It will automatically place the organization in user.organization
, you shouldn't be juggling foreign keys manually when working with ActiveRecord objects:
before_validation :build_default_organization
def build_default_organization
build_organization(name: email, uuid: UUIDTools::UUID.random_create.to_s)
end
Rails will automatically save this record for you, inside a transaction that includes the saving of the parent User
record, and it will automatically set all the foreign keys correctly.
Another option is to use accepts_nested_attributes
and pass through organization_attributes
when you're creating your user.
class User < ApplicationRecord
belongs_to :organization, :autosave => true
accepts_nested_attributes_for :organization
end
User.create!(
email: 'test_user@test.com',
organization_attributes: {
name: 'test_user@test.com',
uuid: UUIDTools::UUID.random_create.to_s
)
)