Jerry
Jerry

Reputation: 198

rails 4 nested form, 1 model dont associate id

I have an order form and when an order is created, a new customer is created as well. For this I have the following models:

class Customer < ActiveRecord::Base
    has_many :orders 
    has_many :subscriptions, through: orders
end

class Order < ActiveRecord::Base
    belongs_to :customer
    has_many :subscriptions

    accepts_nested_attributes_for :customer
    accepts_nested_attributes_for :subscriptions
end

class Subscription< ActiveRecord::Base
    belongs_to :order
    belongs_to :customer
end

On my order page I have this form:

= simple_form_for(@order) do |f|
    = render 'order_fields', f: f 
    = f.simple_fields_for :subscriptions do |subscription| 
        = render 'subscription_fields', subscription: subscription
    = f.simple_fields_for :customer do |customer| 
        = render 'customer_fields', customer: customer 
    = f.button :submit

In my OrderController I have:

def new
    @order = Order.new
    @order.build_customer
    @order.subscriptions.build
end

def create
    @order = Order.new(order_params)

    if @order.save       
        (.... etc ...)
end

private
    def order_params
        params.require(:order).permit(
            :amount,
            customer_attributes: [ :id, :email, :password, :password_confirmation], 
            subscriptions_attributes: [ :id, :product_id, :customer_id])
    end

Almost everything goes well:
- User is created
- Order is created and has customer_id = User.id
- Subscription is created and has order_id = Order.id

But somehow it wont associate the subscription to the customer :(
I keep having Subscription.customer_id = nil

Can someone please point me in the right direction? Is there something wrong in the models? or in the controllers? I have no idea anymore where to look.

Upvotes: 0

Views: 98

Answers (2)

Jerry
Jerry

Reputation: 198

Thanks pdobb! I got it working now with adding in the order.controller:

def create
    @order = Order.new(order_params)

    if @order.save       
       @order.subscriptions.each { subscription| subscription.update_column(:customer_id, @order.customer.id) }
end

Upvotes: 0

pdobb
pdobb

Reputation: 18037

Your relationships are set up a little different. Instead of creating a customer_id field on Subscription, I'd expect you'd just have a has_one :customer, through: :order.

If you do this you won't have need for a customer_id attribute on your Subscription model anymore. And if you want the id of the customer from the world-view of a subscription you'd call subscription.customer.id.

You may also want to add inverse_of designations for your relationships in your models (always a good practice to minimize reloading of models from the database).

So, in total, I'd recommend:

class Customer < ActiveRecord::Base
  has_many :orders, inverse_of: :customer
  has_many :subscriptions, through: orders
end

class Order < ActiveRecord::Base
  belongs_to :customer, inverse_of: :orders
  has_many :subscriptions, inverse_of: :order

  accepts_nested_attributes_for :customer
  accepts_nested_attributes_for :subscriptions
end

class Subscription< ActiveRecord::Base
  belongs_to :order, inverse_of: :subscriptions
  has_one :customer, through: :order # THIS IS THE KEY CHANGE
end

Oh, and then you can remove the customer_id from the permitted attributes for subscriptions_attributes.

UPDATE

Given that Subscription#customer_id is meant to be disjointed from the Customer -> Order -> Subscription relationship... Ignore the above (except for perhaps the inverse_of stuff) and... You should be able to do:

class Customer < ActiveRecord::Base
  has_many :subscriptions, through: :orders, after_add: :cache_customer_id

  private

  def cache_customer_id(subscription)
    subscription.update_column(:customer_id, self.id)
  end
end

Upvotes: 1

Related Questions