Search code examples
ruby-on-railshas-and-belongs-to-many

Add records with HABTM


I am trying to allocate multiple categories to products using the below method

class Category < ActiveRecord::Base    
  has_many :categories_products
  has_many :products, :through => :categories_products

  validates :name,    presence: true, length: { maximum: 255 }
end

class CategoryProduct < ActiveRecord::Base
  belongs_to :category
  belongs_to :product
end

class Product < ActiveRecord::Base
  has_many :categories_products
  has_many :categories, :through => :categories_products
end

Products Controller

def new
  form_info
  if @categories.empty?
    flash[:notice] = 'You must create a category before you create a product.'
    redirect_to new_admin_merchandise_prototype_url
  else
    @product = Product.new
    @product.categories << @categories
    # @product.category  = Category.new
  end
end

Products View

.mdl-grid
  .mdl-cell.mdl-cell--12-col
    h3.mdl-typography--display-1.teal-heading= t('.title')
.mdl-grid
  .mdl-cell.mdl-cell--12-col.card-item-type--volume
    .mdl-textfield.mdl-js-textfield.mdl-textfield--floating-label.mdl-cell.mdl-cell--12-col
      = form.text_field :name, class: 'mdl-textfield__input', required: true
      = form.label :name, class: 'mdl-textfield__label'
    .mdl-textfield.mdl-js-textfield.mdl-textfield--floating-label.mdl-cell.mdl-cell--12-col
      = form.text_area :keywords, class: 'mdl-textfield__input'
      = form.label :keywords, class: 'mdl-textfield__label'
    .mdl-textfield.mdl-js-textfield.mdl-textfield--floating-label.mdl-cell.mdl-cell--12-col
      = form.text_area :description, class: 'mdl-textfield__input'
      = form.label :description, class: 'mdl-textfield__label'
.mdl-grid
  .mdl-cell.mdl-cell--6-col
    h3.mdl-typography--display-1.teal-heading Categories
.mdl-grid
  .mdl-cell.mdl-cell--12-col.card-item-type--volume
    .mdl-grid
      - @categories.each_slice((@categories.count/3).ceil) do |cg|
        .mdl-cell.mdl-cell--3-col
          - cg.each do |c|
            label.mdl-checkbox.mdl-js-checkbox.mdl-js-ripple-effect.mdl-cell.mdl-cell--12-col for=c.id
              = form.check_box :categories, :class => 'mdl-checkbox__input', :id => c.id
              span.mdl-checkbox__label= c.name.titlecase
.mdl-grid
  .mdl-cell.mdl-cel--12-col
    = form.submit class: 'mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent'

Error

NameError: uninitialized constant Product::CategoriesProduct

Solution

  • You are facing a naming issue:

    Your model is named CategoryProduct. Check your migration where you created the table: It will most likely be something like:

    create_table :category_products do |t|
      t.belongs_to :product
      t.belongs_to :category
    
      t.timestamps
    end
    

    The important part here is :category_products. The same table name will show up in your db/schema.rb

    If you have not seen it already: the Product is singular while the Category is pluralized.

    The issue then comes up in the relation:

    has_many :categories_products
    

    In the process of finding the correct model rails uses .singularize., which only singularizes the last word in a string.

    Running 'categories_products'.singularize returns

    => "categories_product"

    Hence rails is looking for a model CategoriesProduct and does not find it. Check your error message ;)

    There would be three ways to solve this:

    1) Change your table name

    Undo your migration with rake db:rollback, change the name to defined table name to category_products and then migrate again.

    2) Change your model name

    change the file to categories_product and the model to CategoriesProduct

    3) Specify the join_table in the relation

    has_many :categories_products,  :join_table => :category_products
    

    don't do this one - it is an ugly fix - go for 1) or 2)