Reputation: 28783
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
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
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
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
Reputation: 2675
Try this. It should work
Client.create!(name: 'Client1').client_users.new([{user_id: User.first},{user_id: User.second}])
Upvotes: 1
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
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