sutee
sutee

Reputation: 12828

Nested forms with multiple table inheritance

How do I build a nested form for objects using multiple table inheritance in rails? I am trying to make a nested form to create an object using a model with a has_many relationship to another set of models that feature multi-table inheritance. I am using formtastic and cocoon for the nested form and the act_as_relation gem to implement the multiple table inheritance.

I have the following models:

class Product < ActiveRecord::Base
 acts_as_superclass
 belongs_to :store
end

class Book < ActiveRecord::Base
     acts_as :product, :as => :producible
end

class Pen < ActiveRecord::Base
     acts_as :product, :as => :producible acts_as :product, :as => :producible
end

class Store < ActiveRecord::Base
    has_many :products
    accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
end'

For this example, the only unique attribute that book has compared to other products is an author field. In reality, I have a number of unique attributes for book which is why I chose multi-table inheritance over the more commonplace single table inheritance.

I am trying to create a nested form that allows you to create a new store with products. Here's my form:

<%= semantic_form_for @store do |f| %>
  <%= f.inputs do %>
    <%= f.input :name %>

    <h3>Books/h3>
    <div id='books'>
    <%= f.semantic_fields_for :books do |book| %>
      <%= render 'book_fields', :f => book %>
    <% end %>
          <div class='links'>
      <%= link_to_add_association 'add book', f, :books %>
      </div>

  <% end %>
<%= f.actions :submit %>
<% end %>

And the book_fields partial:

<div class='nested-fields'>
  <%= f.inputs do %>
    <%= f.input :author %>
    <%= link_to_remove_association "remove book", f %>
  <% end %>
</div>

I get this error:

undefined method `new_record?' for nil:NilClass

Based on reading the issues on the github page for act_as_relation, I thought about making the relationship between store and books more explicit:

class Product < ActiveRecord::Base
 acts_as_superclass
 belongs_to :store
 has_one :book
 accepts_nested_attributes_for :book, :allow_destroy => true, :reject_if => :all_blank
end

class Book < ActiveRecord::Base
     belongs_to :store
     acts_as :product, :as => :producible
end

class Store < ActiveRecord::Base
        has_many :products
        has_many :books, :through => :products
        accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
        accepts_nested_attributes_for :books, :allow_destroy => true, :reject_if => :all_blank
    end

Now, I get a silent error. I can create new stores using the form, and cocoon allows me to add new book fields, but when I submit the store gets created but not the child book. When, I go through the `/books/new' route, I can create a new book record that spans (the products and books table) with no problem.

Is there a workaround to this problem? The rest of the code can be found here.

Upvotes: 4

Views: 1116

Answers (2)

cdpalmer
cdpalmer

Reputation: 668

You might want to consider creating your own form object. This is a RailsCast pro video, but here are some of the examples in the ASCIIcast:

def new
  @signup_form = SignupForm.new(current_user)
end

This signup form can include relations to your other objects, just as you would in your original controller code:

class SignupForm
    # Rails 4: include ActiveModel::Model
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  validates_presence_of :username
  validates_uniqueness_of :username
  validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
  validates_length_of :password, minimum: 6

  def persisted?
    false
  end

    def subscribed
    subscribed_at
  end

  def subscribed=(checkbox)
    subscribed_at = Time.zone.now if checkbox == "1"
  end

  def generate_token
    begin
      self.token = SecureRandom.hex
    end while User.exists?(token: token)
  end
end

Here is the link to the RailsCast. Getting a pro membership might be worth your time. I have been getting lucky with a membership through www.codeschool.com where you can get 'prizes' when you finish courses:

RailsCast: http://railscasts.com/episodes/416-form-objects

Upvotes: 1

rlecaro2
rlecaro2

Reputation: 755

Maybe you could:

  • Build the books relation manually on your stores_controller#new action

    @store.books.build

  • Store manually the relation on you stores_controller#create action

    @store.books ... (not really confident on how to achieve it)

Keep us posted.

Upvotes: 2

Related Questions