Cameron
Cameron

Reputation: 28783

Creating objects with associations in Rails

In my Rails app I have Clients and Users. And Users can have many Clients.

The models are setup as so:

class Client < ApplicationRecord
  has_many :client_users, dependent: :destroy
  has_many :users, through: :client_users
end

class User < ApplicationRecord
  has_many :client_users, dependent: :destroy
  has_many :clients, through: :client_users
end

class ClientUser < ApplicationRecord
  belongs_to :user
  belongs_to :client
end

So if I wanted to create a new client that had the first two users associated with it how would I do it?

e.g.

Client.create!(name: 'Client1', client_users: [User.first, User.second])

Trying that gives me the error:

ActiveRecord::AssociationTypeMismatch: ClientUser(#70142396623360) expected, got #<User id: 1,...

I also want to do this for an RSpec test. e.g.

user1 = create(:user)
user2 = create(:user)

client1 = create(:client, client_users: [user1, user2])

How do I create a client with associated users for in both the Rails console and in an RSpec test?

Upvotes: 1

Views: 120

Answers (6)

coreyward
coreyward

Reputation: 80041

The reason you're getting a mismatch is because you're specifying the client_users association that expects ClientUser instances, but you're passing in User instances:

# this won't work
Client.create!(client_users: [User.first, User.second])

Instead, since you already specified a users association, you can do this:

Client.create!(users: [User.first, User.second])

There's a simpler way to handle this, though: ditch the join model and use a has_and_belongs_to_many relationship. You still need a clients_users join table in the database, but you don't need a ClientUser model. Rails will handle this automatically under the covers.

class Client < ApplicationRecord
  has_and_belongs_to_many :users
end

class User
  has_and_belongs_to_many :clients
end

# Any of these work:
client = Client.new(name: "Kung Fu")
user = client.users.new(name: "Panda")
client.users << User.new(name: "Nemo")
client.save # => this will create two users and a client, and add two records to the `clients_users` join table

Upvotes: 0

Hirurg103
Hirurg103

Reputation: 4953

You can do this with the following code:

user1 = create(:user)
user2 = create(:user)

client1 = create(:client, users: [user1, user2])

See ClassMethods/has_many for the documentation

collection=objects

Replaces the collections content by deleting and adding objects as appropriate. If the :through option is true callbacks in the join models are triggered except destroy callbacks, since deletion is direct.

If you are using factory_girl you can add trait :with_users like this:

FactoryGirl.define do
  factory :client do

    trait :with_two_users do
      after(:create) do |client|
        client.users = create_list :user, 2
      end
    end

  end
end

Now you can create a client with users in test like this:

client = create :client, :with_two_users

Upvotes: 1

Nimish Gupta
Nimish Gupta

Reputation: 3175

You can make use of after_create callback

class Client < ApplicationRecord
  has_many :client_users, dependent: :destroy
  has_many :users, through: :client_users

  after_create :add_users

  private def add_users
    sef.users << [User.first, User.second]
  end
end

Alternatively, A simpler approach would be

Client.create!(name: 'Client1', user_ids: [User.first.id, User.second.id])

Upvotes: 0

krishna raikar
krishna raikar

Reputation: 2675

Try this. It should work

Client.create!(name: 'Client1').client_users.new([{user_id: User.first},{user_id: User.second}])

Upvotes: 1

marmeladze
marmeladze

Reputation: 6564

If you do not want to accept_nested_attributes for anything, as documented here you can also pass block to create.

Client.create!(name: 'Client1') do |client1| 
  client1.users << [User.find(1), User.find(2), User.find(3)]     
end

Upvotes: 2

Anand
Anand

Reputation: 6531

accepts_nested_attributes_for :users

and do as so:

Client.create!(name: 'Client1', users_attributes: { ........ })

hope this would work for you.

Upvotes: 0

Related Questions