malcoauri
malcoauri

Reputation: 12179

Create has_many :through association in Rails 4 with building a new object

There is the following code:

def create
    @business = user.businesses.build(business_params)
    if @business.save
        business.set_logo_url
        redirect_to admin_businesses_path, flash: { notification: 'New business has been created successfully' }
    else
        render 'new'
    end
end

Before this code works; user "has_many" businesses and all worked good, but now user "has_many :through" and this code created a new business but not relatiionship! How can I fix this code saving this business-logic? THanks in advance.

Upvotes: 2

Views: 2179

Answers (3)

Richard Peck
Richard Peck

Reputation: 76774

but now user "has_many :through"

This is the core of the issue here - Rails does many things well, but it cannot read minds yet

The problem you have is that you're trying to build an association which either doesn't exist, or is part of another association (the :through) argument. Let me explain:


has_many

When you .build an object - it has to be associated to the "parent" object you're working with. The has_many association is directly associated with the parent object, allowing you to .build it singularly:

#app/models/user.rb
class User < ActiveRecord::Base
   has_many :businesses
end

This will give you the ability to perform the following: @user.businesses.build

The problem you have now is that using through means your businesses object is not directly associated to your parent User- it's related through another object. This means that in order to build this deeper dependent object, you have first build the through object:


through

You've not given us your associations, but say they were like so:

#app/models/user.rb
class User < ActiveRecord::Base
   has_many :business_users
   has_many :businesses, through: : business_users
end

#app/models/business.rb
class Business < ActiveRecord::Base
   has_many :business_users
   has_many :users, through: :business_users
end

#app/models/business_user.rb
class BusinessUser < ActiveRecord::Base
   belongs_to :business
   belongs_to :user
end

Now you'll have to build the associations slightly differently:

@user.business_users.build.build_business

See how you not have to call the "join" model?

Before, you could just call the businesses association directly. But as you are going through another model, this will have to be built as well.


Form

Translating into a form, you'll end up with this setup:

#app/controllers/users_controller.rb
class UsersController < ApplicationController
   def new
       @user = User.new
       @user.business_users.build.build_business
   end

   def create
       @user = User.new user_params
       @user.save
   end

   private

   def user_params
      params.require(:user).permit(:user, :params, business_users_attributes: [business_attributes: []])
   end
end
#app/views/users/new.html.erb
<%= form_for @user do |f| %>
   <%= f.fields_for :business_users do |bu| %>
      <%= bu.fields_for :user do |u| %>
          <%= u.text_field ... %>
      <% end %>
   <% end %>
   <%= f.submit %>
<% end %>

Upvotes: 4

Nitin
Nitin

Reputation: 7366

Try use create instead of build like this:

def create
    @business = user.businesses.create(business_params)
    if @business.save
        business.set_logo_url
        redirect_to admin_businesses_path, flash: { notification: 'New business has been created successfully' }
    else
        render 'new'
    end
end

Upvotes: 0

Ivan Shamatov
Ivan Shamatov

Reputation: 1416

Try to add :inverse_of in your User model

class User < ActiveRecord::Base

  has_many :business_types, inverse_of: :user #let your relationship model to be BusinessType
  has_many :businesses, :through => :business_types
  ...
end

Also don't forget to add accepts_nested_attributes_for in your relationship model too

Upvotes: 0

Related Questions