Reputation: 991
I have a model named Entry, which has many Categories. The page where I create/edit the Entry has checkboxes for every Category.
When I am editing an Entry, everything works okay. When I create the Entry, I get as an error for the @entry:
:entry_categories=>["is invalid"]
My thinking is that rails can't create the entry_categories because it doesn't know the id of the Entry ( which it shouldn't, it hasn't been assigned an id yet ).
I feel like this is a very common thing to try to do. I haven't been able to find an answer though. Here comes the code spamming, there must be something I am missing and hopefully some more experienced eyes can see it.
entry.rb
class Entry < ActiveRecord::Base
validates_presence_of :contents
validates_presence_of :title
has_many :entry_categories, dependent: :destroy
has_many :categories, through: :entry_categories
belongs_to :book
validates_presence_of :book
end
entry_category.rb
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :entry
validates_presence_of :category
end
category.rb
class Category < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
has_many :entry_categories, dependent: :destroy
has_many :entries, through: :entry_categories
end
entries_controller.rb
class EntriesController < ApplicationController
before_action :find_entry, only: [ :show, :edit, :update, :destroy ]
before_action :find_book, only: [ :new, :create, :index ]
before_action :authenticate_admin!, only: [:new, :create, :edit, :update, :destroy ]
def new
@entry = Entry.new
end
def create
@entry = @book.entries.new( entry_params )
if @entry.save
redirect_to entry_path( @entry ), notice: 'Entry Created'
else
render :new
end
end
def show
@categories = Category.joins( :entry_categories ).where( "entry_categories.entry_id = #{@entry.id} " ).select( "name, categories.id " )
@category_class = @categories.first.name.downcase.gsub( / /, '_' ) if @categories.any?
end
def index
@entries = @book ? @book.entries : Entry.all
end
def edit
end
def update
if @entry.update( entry_params )
redirect_to entry_path( @entry ), notice: 'Entry Updated'
else
render :edit
end
end
def destroy
@book = @entry.book
@entry.destroy
redirect_to book_path( @book ) , notice: 'Entry Destroyed'
end
protected
def entry_params
params.require(:entry).permit( :title, :contents, :year, :month, :day, category_ids: [] )
end
def find_entry
@entry = Entry.find( params[:id] )
end
def find_book
@book = Book.find( params[ :book_id ] )
rescue
@book = nil
end
end
_form.html.erb
<%= form_for [ @book, @entry ] do | form | %>
<%= content_tag :p, title %>
<%= form.text_field :title, placeholder: 'Title', required: true %>
<%= form.number_field :year, placeholder: 'Year' %>
<%= form.number_field :month, placeholder: 'Month' %>
<%= form.number_field :day, placeholder: 'Day' %>
<%= form.text_area :contents %>
<fieldset>
<legend>Categories</legend>
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
</fieldset>
<%= form.submit %>
<% end %>
So again, the entry_categories are invalid in the create method, in the update they are fine. It's the same html file.
There must be some way to tell rails to save the Entry before trying to save the EntryCategory?
Thanks.
Upvotes: 1
Views: 63
Reputation: 991
I managed to get this working by taking the validations out of EntryCategory:
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :category
end
I'm not particularity happy about this solution, and would still appreciate other thoughts.
Upvotes: 1
Reputation: 2508
Try this:
replace this code:
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
with:
<% Category.all.order(name: :asc).each do |category| %>
<div>
<%= check_box_tag "entry[category_ids][]", category.id %>
<%= category.name %>
</div>
you can format it with fieldset
instead of div
Upvotes: 0
Reputation: 73589
I think you can use any of the following approach:
You can use autosave functionality of Active Record association. By this, when you will save EntryCategory
, it will automatically save Entry
as well.
class EntryCategory < ActiveRecord::Base
belongs_to :entry , autosave: true
#rest of the code
end
You can also use before_save callback of active record. by this, whenever you will save EntryCategory
, it will first call a specified method, than proceed with saving.
class EntryCategory < ActiveRecord::Base
before_save :save_associated_entries
#rest of the code
def save_associated_entries
# code to save associated entries here
end
end
Upvotes: 0