Ruby on rails newbie here. Trying to create a starter blog app and having trouble with many to many association between my models.
I have 2 models - Post, Category that have a many to many association between each other.
My problem: When I create a new post, the Post gets saved but the post-category association does not get saved in the categories_posts table.
My code is as below.
I appreciate your inputs on this.
post.rb
class Post < ActiveRecord::Base
validates_presence_of :title, :body, :publish_date
belongs_to :user
has_and_belongs_to_many :categories
end
category.rb
class Category < ActiveRecord::Base
validates_presence_of :name
has_and_belongs_to_many :posts
end
categories_posts.rb
class CategoriesPosts < ActiveRecord::Base
end
Migrations - create_posts.rb
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.date :publish_date
t.integer :user_id
t.timestamps
end
end
end
Migrations - create_categories.rb
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
Migrations - create_categories_posts.rb
class CreateCategoriesPosts < ActiveRecord::Migration
def change
create_table :categories_posts do |t|
t.integer :category_id
t.integer :post_id
t.timestamps
end
end
end
Post Controller - create and new methods
#GET /posts/new
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
#User id is not a form field and hence is not assigned in the view. It is assigned when control is transferred back here after Save is pressed
@post.user_id = current_user.id
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: @post }
else
format.html { render action: 'new' }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
Post View(for creating a new Post):
<%= simple_form_for @post, :html => { :class => 'form-horizontal' } do |f| %>
<%= f.input :title %>
<%= f.input :body %>
<%= f.input :publish_date %>
<%= f.association :categories, :as => :check_boxes %>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
posts_path, :class => 'btn' %>
</div>
<% end %>
Thanks, Mike
When using the has_and_belongs_to_many
association you need a unique index on your join table. Your migration should look like this:
class CreateCategoriesPosts < ActiveRecord::Migration
def change
create_table :categories_posts do |t|
t.integer :category_id
t.integer :post_id
t.timestamps
end
add_index :categories_posts, [:category_id, :post_id]
end
end
You can also get rid of the CategoriesPost model, that is only needed if you wanted to implement a :has_many, :through
association. That should answer your question.
And just to be thorough, if you wanted to use a :has_many, :through
association with a CategoriesPost model you can implement that like so:
class Post < ActiveRecord::Base
has_many :categoriesposts
has_many :categories, :through => :categoriesposts
end
class Category < ActiveRecord::Base
has_many :categoriesposts
has_many :posts, :through => :categoriesposts
end
class CategoriesPost < ActiveRecord::Base
belongs_to :post
belongs_to :category
end
Implementing this method allows you to add more attributes to your categoriespost model if you wanted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With