has_many build AssociationTypeMismatch Error - ruby-on-rails

Another newb question. A Band has_many Albums. I'm getting the error:
ActiveRecord::AssociationTypeMismatch (Band(#70076964285020) expected,
got "1" which is an instance of String(#12787800)):
app/controllers/albums_controller.rb:20:in `create'
... which is the build line in #create
albums controller
def new
binding.pry
#band = Band.find(params[:band])
authorize #band, :admin?
#album = Album.new
respond_to do |format|
format.js
end
end
def create
binding.pry
#band = Band.find(params[:album][:band].to_i)
authorize #band, :admin?
#album = #band.albums.build(album_params)
if #album.save
#albums = #band.albums
##eps = #band.eps
#songs = #band.songs
respond_to do |format|
format.js
end
else
#fail = "fail"
respond_to do |format|
format.js
end
end
end
def album_params
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
end
the form:
<%=simple_form_for(#album, remote: true, :authenticity_token => true, format: :js) do |f| %>
<%= f.hidden_field :band, :value => #band.id %>
<%= f.input :album_name %>
<%= f.input :album_release_date %>
<%= f.input :etc %>
<div id="albumsubmit">
<div class="form-actions">
<%= f.button :submit, "Create Album", class: "btn btn-primary" %>
</div>
</div>
schema
create_table "albums", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "band_id"
t.string "album_name"
t.string "album_release_date"
t.index ["band_id"], name: "index_albums_on_band_id"
end

when you passing to save method param with that have has_many Association he expect the instance of "band", just set the param name to band_id
here:
<%= f.hidden_field :band, :value => #band.id %>
to:
<%= f.hidden_field :band_id, :value => #band.id %>
and here:
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
to:
params.require(:album).permit(:band_id, :album_name, :album_release_date, :etc)

Related

Undefined method error in rails?

I have a model where students can enter information about themselves and others can view them. Since there are a lot of profiles, I have added a search section too. If they want only CS majors, they can select from a drop down. I had things like major, college... today I added "level", which is level of education and I get an error (Error shown at the end). I know this is long, but any help would be really really useful. Thanks!
(Userinfo is the student model)
My controller looks like this:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy, :log_impression]
def index
#userinfors = Userinfo.search(params[:search])
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def show
end
def new
#userinformation = current_user.build_userinfo
end
def create
#userinformation = current_user.build_userinfo(userinfo_params)
if #userinformation.save
redirect_to userinfo_path(#userinformation)
else
render 'new'
end
end
def edit
end
def update
if #userinformation.update(userinfo_params)
redirect_to userinfo_path(#userinformation)
else
render 'edit'
end
end
def destroy
#userinformation.destroy
redirect_to root_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major, :token, :skills, :level)
end
def find_userinfo
#userinformation = Userinfo.friendly.find(params[:id])
end
end
My information fill out form (please note how the way of input is different in major and level of edu, maybe thats the problem?):
<%= simple_form_for #userinformation, :html => { :multipart => true } do |f| %>
<%= f.input :name, required: true, label: 'Full name' %>
<%= f.input :college, required: true, label: 'Name of college' %>
<%= f.input :gpa, required: true, label: 'Enter GPA' %>
<%= f.input :email, required: true, label: 'Enter email address' %>
<%= f.input :major, required: true, label: 'Major' %>
<%= f.input :level, collection: ["College undergrad", "College grad"], required: true, label: 'Level of education' %>
<%= f.input :skills, required: true, label: 'Skills' %>
<%= f.button :submit, 'Create account' %>
<% end %>
Search controller:
class SearchesController < ApplicationController
def new
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
end
private
def search_params
params.require(:search).permit(:keyword, :college, :min_gpa, :major, :token, :level )
end
end
Search model:
class Search < ActiveRecord::Base
def search_userinfos
userinfos = Userinfo.all
userinfos = userinfos.where(["name LIKE ?","%#{keyword}%"]) if keyword.present?
userinfos = userinfos.where(["college LIKE ?", college]) if college.present?
userinfos = userinfos.where(["cast(gpa as numeric) >= ?", min_gpa]) if min_gpa.present?
userinfos = userinfos.where(["major LIKE ?", major]) if major.present?
userinfos = userinfos.where(["level LIKE ?", level]) if level.present?
userinfos = userinfos.order("gpa")
return userinfos
end
end
This is where the search section is displayed (index view):
<%= simple_form_for #search do |s| %>
<%= s.input :keyword, label: 'Name' %>
<label>College</label>
<%= s.select :college, options_for_select(#college), :include_blank => true %>
<%= s.input :min_gpa, label: "Minimum GPA" %>
<label>Major</label>
<%= s.select :major, options_for_select(#major), :include_blank => true %>
<label>Job type</label>
<%= s.select :level, options_for_select(#level), :include_blank => true %>
<%= s.button :submit, "Search" %>
<% end %>
The error I get when I try to visit the index view:
NoMethodError in Userinfos#index
Showing app-path/index.html.erb where line #25 raised:
undefined method `level' for #<Search:0x000000139d6c38>
Line 25 is:
<%= s.select :level, options_for_select(#level), :include_blank => true %>
Everything works if I remove that line. When I show the education level in the user profile page, it works perfectly.
Schema for searches:
create_table "searches", force: :cascade do |t|
t.string "keyword"
t.string "college"
t.decimal "min_gpa"
t.string "major"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
As you commented, you do not have a level attribute in your Search model.
rails g migration add_level_to_searches level:string
And of course
rails db:migrate
Happy to help!

Paperclip upload causes update.js.erb to output code as text

I've been having this problem with Paperclip for a while. New/Edit form submits and saves/updates, but create.js and update.js renders the code (that normally would be a view) as text to http://localhost:3000/albums/9.js (if it's the 9th album being updated), instead of a view in a modal on localhost:3000.
so instead of rendering a view, i get this text on /albums/9.js: $("#actionEdit").html("Album Updated.")
album model
belongs_to :band
has_many :songs
has_attached_file :album_artwork_1,
styles: { thumb: ["64x64#", :jpg] },
convert_options: { thumb: "-strip",
original: "-strip" }
has_attached_file :album_artwork_2,
styles: { thumb: ["64x64#", :jpg] },
convert_options: { thumb: "-strip",
original: "-strip" }
has_attached_file :album_artwork_3,
styles: { thumb: ["64x64#", :jpg] },
convert_options: { thumb: "-strip",
original: "-strip" }
do_not_validate_attachment_file_type :album_artwork_1
do_not_validate_attachment_file_type :album_artwork_2
do_not_validate_attachment_file_type :album_artwork_3
validates :album_name, presence: true
validates :album_release_date, presence: true
albums controller
def new
binding.pry
#band = Band.find(params[:band])
authorize #band, :admin?
#album = Album.new
respond_to do |format|
format.js
end
end
def create
#band = Band.find(params[:album][:band].to_i)
authorize #band, :admin?
#album = #band.albums.build(album_params)
if #album.save
#records = []
#band.albums.each do |album|
#records << album
end
#band.eps.each do |ep|
#records << ep
end
#band.songs.each do |song|
#records << song
end
#records_ordered = #records #.sort.reverse!
respond_to do |format|
format.js
end
else
respond_to do |format|
format.js
end
end
end
def edit
#band = Band.find(params[:band])
authorize #band, :admin?
#album = Album.find(params[:id])
respond_to do |format|
format.js
end
end
def update
binding.pry
#band = Band.find(params[:album][:band])
authorize #band, :admin?
#album = Album.find(params[:id])
if #album.update(album_params)
respond_to do |format|
format.js
end
else
#fail = "w4rd"
respond_to do |format|
format.js
end
end
end
form
<%=simple_form_for(#album, remote: true, :authenticity_token => true, format: :js) do |f| %>
<% if #album.album_artwork_1.present? %>
<%= link_to image_tag(#album.album_artwork_1.url(:thumb), class: 'media-object'), #album.album_artwork_1.url, target: '_blank' %>
<% end %>
<div class="form-group">
<%= f.input :album_artwork_1, as: :file %>
</div>
<% if #album.album_artwork_2.present? %>
<%= link_to image_tag(#album.album_artwork_2.url(:thumb), class: 'media-object'), #album.album_artwork_2.url, target: '_blank' %>
<% end %>
<div class="form-group">
<%= f.input :album_artwork_2, as: :file %>
</div>
<% if #album.album_artwork_3.present? %>
<%= link_to image_tag(#album.album_artwork_3.url(:thumb), class: 'media-object'), #album.album_artwork_3.url, target: '_blank' %>
<% end %>
<div class="form-group">
<%= f.input :album_artwork_3, as: :file %>
</div>
<br><br>
<div id="albumsubmit">
<% if params[:action] === "edit" %>
<div class="form-actions">
<%= f.button :submit, "Update Album", class: "btn btn-primary" %>
</div>
<% else %>
<div class="form-actions">
<%= f.button :submit, "Create Album", class: "btn btn-primary" %>
</div>
<% end %>
</div>
<% end %>
update.js.erb
$("#actionEdit").html("<%= j render 'updated' %>")
schema
create_table "albums", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "album_artwork_1_file_name"
t.string "album_artwork_1_content_type"
t.integer "album_artwork_1_file_size"
t.datetime "album_artwork_1_updated_at"
t.string "album_artwork_2_file_name"
t.string "album_artwork_2_content_type"
t.integer "album_artwork_2_file_size"
t.datetime "album_artwork_2_updated_at"
t.string "album_artwork_3_file_name"
t.string "album_artwork_3_content_type"
t.integer "album_artwork_3_file_size"
t.datetime "album_artwork_3_updated_at"
params coming into #update
[1] pry(#<AlbumsController>)> params
=> <ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"B/g/tnhObi6YmY6gqY0hTzJyiewshxVN6S1Dv+1yspGiRQZAlr8qdRiMtDyOh1/1VYvmr1FW9AsClLnW6xt9rg==", "album"=>{"band"=>"1", "album_name"=>"Test", "album_release_date"=>"2017-04-29", "produced_by"=>"", "recorded_at"=>"", "engineered_by"=>"", "second_engineers"=>"", "mixed_by"=>"", "mastered_by"=>"", "artwork_by"=>"", "photography_by"=>"", "art_direction_by"=>"", "construction_by"=>"", "booking"=>"", "litigation"=>"", "additional_musicians"=>"", "copyright"=>"", "websites"=>"", "contacts"=>"", "other"=>"", "album_artwork_2"=>#<ActionDispatch::Http::UploadedFile:0x007f681a3b4ca8 #tempfile=#<Tempfile:/tmp/RackMultipart20170416-32079-mie4xo.jpg>, #original_filename="c.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"album[album_artwork_2]\"; filename=\"c.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Update Album", "controller"=>"albums", "action"=>"update", "id"=>"7", "format"=>"js"} permitted: false>
[2] pry(#<AlbumsController>)>
Apparently you can't upload via remote: true without the gem remotipart, which resolved the issue.

Rails 4 multiple image uploading using carrierwave

How can I upload multiple images using CarrierWave?
I followed "drjorgepolanco" answer from
Rails 4 multiple image or file upload using carrierwave
but I keep getting a error
undefined method `[]' for #<AssignmentpicUploader:0x007eff7831a4d8>
Extracted source (around line #25):
ActionView::Template::Error (undefined method `[]' for #<AssignmentpicUploader:0x007eff7831a4d8>):
22:
23:
24: <div id="img">
25: <%= image_tag #assignment.picture[1].url(:large) %>
26: </div>
27:
28: </br>
app/views/assignments/show.html.erb:25
assignment.rb
class Assignment < ActiveRecord::Base
validates :name, length: { minimum: 1, maximum:120 }
validates :description, length: { minimum: 1 }
mount_uploader :picture, AssignmentpicUploader
end
assignments_controller.rb
class AssignmentsController < ApplicationController
before_action :authenticate_user!
before_filter :admin_access, only: [:new, :create, :edit, :update, :destroy]
def index
#assignments = Assignment.all.order("created_at DESC")
end
def show
#assignment = Assignment.find(params[:id])
end
def new
#assignment = Assignment.new
end
def create
#assignment = current_user.assignments.build
#assignment.name = params[:assignment][:name]
#assignment.description = params[:assignment][:description]
#assignment.picture = params[:assignment][:picture]
if #assignment.save
flash[:notice] = "Assignment was saved successfully."
redirect_to #assignment
else
flash.now[:alert] = "Error creating assignment. Please make sure there is a name and description."
render :new
end
end
def edit
#assignment = Assignment.find(params[:id])
end
def update
#assignment = Assignment.find(params[:id])
#assignment.name = params[:assignment][:name]
#assignment.description = params[:assignment][:description]
#assignment.picture = params[:assignment][:picture]
if #assignment.save
flash[:notice] = "Assignment was updated successfully."
redirect_to #assignment
else
flash.now[:alert] = "Error saving assignment. Please try again."
render :edit
end
end
def destroy
#assignment = Assignment.find(params[:id])
if #assignment.destroy
flash[:notice] = "\"#{#assignment.name}\" was deleted successfully."
redirect_to action: :index
else
flash.now[:alert] = "There was an error deleting the assignment."
render :show
end
end
private
def assignment_params
params.require(:assignment).permit(:name, :description, picture: [])
end
end
assignments/new.html.erb
...
<div class="col-md-8">
<%= form_for #assignment, :html => { :multipart => true } do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter assignment name" %>
</div>
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, rows: 8, class: 'form-control', placeholder: "Enter assignment description" %>
</div>
<div class="form-group">
<%= f.label :picture %>
<%= f.file_field :picture, :multiple => true %>
</div>
<br />
<%= f.submit "Save", class: 'button' %>
<% end %>
</div>
</div>
assignments/show.html.erb
<%= image_tag #assignment.picture[1].url(:large) %>
schema.rb
create_table "assignments", force: :cascade do |t|
t.string "name"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "picture"
end
add_index "assignments", ["user_id"], name: "index_assignments_on_user_id"
Carrierwave documentation says:
Add a column which can store an array. This could be an array column or a JSON column for example. Your choice depends on what your database supports.
Your "picture" is just a string. Add serialize :picture, JSON to your model.

Building nested form questions based on a 3rd model

I want to build a form for entries, where a couple of contest_questions are predicted by the user.
That means I dynamically wants to fetch the contest_questions related to the tipster-contest and then letting the user register an entry with predictions for each of those questions.
How can I do this? As of now I the fields are not showing up since <%= f.fields_for :predictions do |prediction| %>does not execute within the contest_question block and if I change it to contest_question.fields_for ...I get a
undefined method `fields_for' for #
tipster_contest.rb
class TipsterContest < ActiveRecord::Base
belongs_to :bookmaker
belongs_to :game
has_many :entries
has_many :contest_questions
extend FriendlyId
friendly_id :name, use: [:slugged, :history]
enum status: { "Scheduled" => 0, "Open" => 1, "Closed" => 2, "Graded" => 3 }
scope :published?, -> { where(published: :true) }
end
entry.rb
class Entry < ActiveRecord::Base
belongs_to :user
belongs_to :tipster_contest
has_many :predictions
accepts_nested_attributes_for :predictions
end
contest_question.rb
class ContestQuestion < ActiveRecord::Base
belongs_to :tipster_contest
has_many :predictions
end
prediction.rb
class Prediction < ActiveRecord::Base
belongs_to :entry
has_one :contest_question
end
_form.html.erb
<%= form_for(#entry) do |f| %>
<% #contest.contest_questions.order(name: :asc).each do |contest_question| %>
<%= f.fields_for :predictions do |prediction| %>
<div class="field">
<%= prediction.label contest_question.name %>
<%= prediction.select(:prediction, #contest.teams) %>
</div>
<div class="field">
<%= prediction.label :wager_amount %>
<%= prediction.input :wager_amount %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= f.submit "Save entry", :class => "btn btn-success" %> <%= link_to 'Back', bets_path, :class => "btn btn-danger" %>
</div>
<% end %>
And the relevant part of my schema.rb if that would be needed:
create_table "contest_questions", force: true do |t|
t.integer "tipster_contest_id"
t.string "name"
t.string "result"
t.integer "min_wager"
t.integer "max_wager"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "entries", force: true do |t|
t.integer "user_id"
t.integer "tipster_contest_id"
t.integer "bankroll"
t.integer "won"
t.string "currency"
t.boolean "entry_valid"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "predictions", force: true do |t|
t.integer "entry_id"
t.integer "contest_question_id"
t.string "prediction"
t.integer "wager_amount"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "tipster_contests", force: true do |t|
t.integer "bookmaker_id"
t.integer "game_id"
t.string "name"
t.string "tournament"
t.integer "status", default: 0
t.text "rules"
t.integer "prizepool"
t.string "currency"
t.text "payout_structure"
t.integer "tipster_wallet"
t.string "teams", default: [], array: true
t.datetime "registration_open"
t.datetime "registration_close"
t.boolean "published", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "logo"
t.text "description"
t.string "slug"
end
add_index "tipster_contests", ["slug"], name: "index_tipster_contests_on_slug", using: :btree
Managed to figure it out and at least get it working. I don't know if the solution is any good in terms of the rails standard. Anyway here it is:
_form.html.erb
<% title("#{#contest.name}") %>
<% description("Join #{#contest.name} and use your eSports knowledge to win extra cash or in-game skins for games like CS:GO, Dota 2, etc.") %>
<p id="notice"><%= notice %></p>
<%= form_for(#entry) do |f| %>
<% if #entry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#entry.errors.count, "error") %> prohibited this entry from being saved:</h2>
<ul>
<% #entry.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field 'tipster_contest_id', :value => #contest.id %>
<div class="t_container">
<div class="t_row">
<div class="t_left"><b>Placement</b></div>
<div class="t_middle"><b>Team</b></div>
<div class="t_right"><b>Wager Amount</b></div>
</div>
<%= f.fields_for :predictions, #predictions do |p| %>
<div class="t_row">
<div class="t_left"><%= #entry.contest_questions.order(:name => :asc)[p.index].name %></div>
<div class="t_middle"><%= p.select :prediction, #contest.teams %></div>
<div class="t_right"><%= p.text_field :wager_amount, label: "Wager amount" %></div>
<%= p.hidden_field 'contest_question_id', :value => #entry.contest_questions.order(:name => :asc)[p.index].id %>
</div>
<% end %>
</div>
</br>
<div class="actions">
<%= f.submit "Save entry", :class => "btn btn-success" %> <%= link_to 'Back', bets_path, :class => "btn btn-danger" %>
</div>
<% end %>
entries_controller.rb
class EntriesController < ApplicationController
before_action :set_entry, only: [:show, :edit, :update, :destroy]
# GET /entries
# GET /entries.json
def index
#entries_open = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 1})
#entries_closed = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 2})
#entries_graded = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 3})
#contests = TipsterContest.where(status: 0..1).includes(:game)
end
# GET /entries/1
# GET /entries/1.json
def show
end
# GET /entries/new
def new
if user_signed_in?
begin
#contest = TipsterContest.friendly.find(params[:contest])
redirect_to tipster_contests_path(#contest), notice: "#{#contest.name} does not accept entries at this point. Registration opens at: #{#contest.registration_open} and closes at: #{#contest.registration_close}." and return true unless #contest.status == "Open"
#entry = Entry.new(tipster_contest: #contest)
#predictions = []
#contest.contest_questions.order(name: :asc).each do |cq|
#predictions << Prediction.new(entry: #entry.id, contest_question_id: cq.id)
end
rescue
redirect_to tipster_contests_path, notice: 'Could not find a tipster-contest with that ID'
end
else
redirect_to new_user_registration_path, notice: 'You need to sign up to enter the tipster contest!'
end
end
# GET /entries/1/edit
def edit
if user_signed_in?
#contest = #entry.tipster_contest
#predictions = #entry.predictions
redirect_to tipster_contests_path, notice: 'This is not your entry to edit' and return true unless #entry.user == current_user
else
redirect_to new_user_registration_path, notice: 'You need to sign up to enter or edit a tipster contest entry!'
end
end
# POST /entries
# POST /entries.json
def create
#entry = Entry.new(entry_params)
#predictions = #entry.predictions
#contest = #entry.tipster_contest
#entry.user = current_user
#entry.won = 0
#entry.bankroll = #contest.tipster_wallet
#entry.currency = #contest.currency
respond_to do |format|
if #entry.save
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /entries/1
# PATCH/PUT /entries/1.json
def update
#contest = #entry.tipster_contest
##predictions = #entry.predictions
respond_to do |format|
if #entry.update(entry_params)
format.html { redirect_to #entry, notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: #entry }
else
format.html { render :edit }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# DELETE /entries/1
# DELETE /entries/1.json
def destroy
#entry.destroy
respond_to do |format|
format.html { redirect_to entries_url, notice: 'Entry was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
#entry = Entry.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params[:entry].permit(:id, :tipster_contest_id, predictions_attributes: [:prediction, :wager_amount, :contest_question_id, :id])
end
end

How to pre-populate collection_check_boxes in an edit form?

GitHub repo: https://github.com/Yorkshireman/mywordlist
I've googled the hell out of this one. I'm sure there's a way, possibly requiring some code inside a html options hash, but I can't work it out. Any ideas?
When visiting the _edit_word_form.html.erb partial for a Word that DOES have one or more categories, the Categories checkboxes are all unchecked, requiring the user to select them again, even if they don't want to change the Categories.
The text fields for the :title and :description ARE pre-populated (thankfully).
_edit_word_form.html.erb:
<%= form_for(#word) do %>
<%= fields_for :word, #word do |word_form| %>
<div class="field form-group">
<%= word_form.label(:title, "Word:") %><br>
<%= word_form.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %>
</div>
<div class="field form-group">
<%= word_form.label(:description, "Definition:") %><br>
<%= word_form.text_area(:description, class: "form-control") %>
</div>
<% end %>
<%= fields_for :category, #category do |category_form| %>
<% if current_user.word_list.categories.count > 0 %>
<div class="field form-group">
<%= category_form.label(:title, "Choose from existing Categories:") %><br>
<%= category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) do |b| %>
<%= b.label(class: "checkbox-inline") { b.check_box + b.text } %>
<% end %>
</div>
<% end %>
<h4>AND/OR...</h4>
<div class="field form-group">
<%= category_form.label(:title, "Create and Use a New Category:") %><br>
<%= category_form.text_field(:title, class: "form-control") %>
</div>
<% end %>
<div class="actions">
<%= submit_tag("Update!", class: "btn btn-block btn-primary btn-lg") %>
</div>
<% end %>
Relevant part of the words/index.html.erb:
<% current_user.word_list.words.alphabetical_order_asc.each do |word| %>
<tr>
<td>
<%= link_to edit_word_path(word) do %>
<%= word.title %>
<span class="glyphicon glyphicon-pencil"></span>
<% end %>
</td>
<td><%= word.description %></td>
<td>
<% word.categories.alphabetical_order_asc.each do |category| %>
<a class="btn btn-info btn-sm", role="button">
<%= category.title %>
</a>
<% end %>
</td>
<td>
<%= link_to word, method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
</td>
</tr>
<% end %>
words_controller.rb:
class WordsController < ApplicationController
before_action :set_word, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /words
# GET /words.json
def index
#words = Word.all
#quotes = Quote.all
end
# GET /words/1
# GET /words/1.json
def show
end
# GET /words/new
def new
#word = current_user.word_list.words.build
end
# GET /words/1/edit
def edit
end
# POST /words
# POST /words.json
def create
#word = Word.new(word_params)
respond_to do |format|
if #word.save
format.html { redirect_to #word, notice: 'Word was successfully created.' }
format.json { render :show, status: :created, location: #word }
else
format.html { render :new }
format.json { render json: #word.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /words/1
# PATCH/PUT /words/1.json
def update
#need to first remove categories from the word
#word.categories.each do |category|
#word.categories.delete category
end
#then push categories in from the category_params
if params["category"].include?(:category_ids)
(params["category"])["category_ids"].each do |i|
next if i.to_i == 0
#word.categories << Category.find(i.to_i) unless #word.categories.include?(Category.find(i.to_i))
end
end
if category_params.include?(:title) && ((params["category"])["title"]) != ""
#word.categories << current_user.word_list.categories.build(title: (params["category"])["title"])
end
respond_to do |format|
if #word.update(word_params)
format.html { redirect_to words_path, notice: 'Word was successfully updated.' }
format.json { render :show, status: :ok, location: #word }
else
format.html { render :edit }
format.json { render json: #word.errors, status: :unprocessable_entity }
end
end
end
# DELETE /words/1
# DELETE /words/1.json
def destroy
#word.destroy
respond_to do |format|
format.html { redirect_to words_url, notice: 'Word was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_word
#word = Word.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def word_params
params.require(:word).permit(:title, :description, :category_ids)
end
def category_params
params.require(:category).permit(:title, :category_ids, :category_id)
end
end
categories_controller.rb:
class CategoriesController < ApplicationController
before_action :set_category, only: [:show, :edit, :update, :destroy]
# GET /categories
# GET /categories.json
def index
#categories = Category.all
end
# GET /categories/1
# GET /categories/1.json
def show
end
# GET /categories/new
def new
#category = current_user.word_list.categories.build
end
# GET /categories/1/edit
def edit
end
# POST /categories
# POST /categories.json
def create
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.html { redirect_to #category, notice: 'Category was successfully created.' }
format.json { render :show, status: :created, location: #category }
else
format.html { render :new }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /categories/1
# PATCH/PUT /categories/1.json
def update
respond_to do |format|
if #category.update(category_params)
format.html { redirect_to #category, notice: 'Category was successfully updated.' }
format.json { render :show, status: :ok, location: #category }
else
format.html { render :edit }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
# DELETE /categories/1
# DELETE /categories/1.json
def destroy
#category.destroy
respond_to do |format|
format.html { redirect_to categories_url, notice: 'Category was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_category
#category = Category.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def category_params
params.require(:category).permit(:title, :word_list_id, :category_ids)
end
end
word_lists_controller.rb:
class WordListsController < ApplicationController
before_action :set_word_list, only: [:show, :edit, :update, :destroy]
def from_category
#selected = current_user.word_list.words.joins(:categories).where( categories: {id: (params[:category_id])} )
respond_to do |format|
format.js
end
end
def all_words
respond_to do |format|
format.js
end
end
# GET /word_lists
# GET /word_lists.json
def index
#word_lists = WordList.all
end
# GET /word_lists/1
# GET /word_lists/1.json
def show
#words = Word.all
#word_list = WordList.find(params[:id])
end
# GET /word_lists/new
def new
#word_list = WordList.new
end
# GET /word_lists/1/edit
def edit
end
# POST /word_lists
# POST /word_lists.json
def create
#word_list = WordList.new(word_list_params)
respond_to do |format|
if #word_list.save
format.html { redirect_to #word_list, notice: 'Word list was successfully created.' }
format.json { render :show, status: :created, location: #word_list }
else
format.html { render :new }
format.json { render json: #word_list.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /word_lists/1
# PATCH/PUT /word_lists/1.json
def update
respond_to do |format|
if #word_list.update(word_list_params)
format.html { redirect_to #word_list, notice: 'Word list was successfully updated.' }
format.json { render :show, status: :ok, location: #word_list }
else
format.html { render :edit }
format.json { render json: #word_list.errors, status: :unprocessable_entity }
end
end
end
# DELETE /word_lists/1
# DELETE /word_lists/1.json
def destroy
#word_list.destroy
respond_to do |format|
format.html { redirect_to word_lists_url, notice: 'Word list was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_word_list
#word_list = WordList.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def word_list_params
params[:word_list]
end
end
word_list.rb:
class WordList < ActiveRecord::Base
belongs_to :user
has_many :words
has_many :categories
end
word.rb:
class Word < ActiveRecord::Base
belongs_to :word_list
has_and_belongs_to_many :categories
validates :title, presence: true
scope :alphabetical_order_asc, -> { order("title ASC") }
end
category.rb:
class Category < ActiveRecord::Base
has_and_belongs_to_many :words
belongs_to :word_list
validates :title, presence: true
scope :alphabetical_order_asc, -> { order("title ASC") }
end
schema.rb:
ActiveRecord::Schema.define(version: 20150609234013) do
create_table "categories", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "word_list_id"
end
add_index "categories", ["word_list_id"], name: "index_categories_on_word_list_id"
create_table "categories_words", id: false, force: :cascade do |t|
t.integer "category_id"
t.integer "word_id"
end
add_index "categories_words", ["category_id"], name: "index_categories_words_on_category_id"
add_index "categories_words", ["word_id"], name: "index_categories_words_on_word_id"
create_table "quotes", force: :cascade do |t|
t.text "content"
t.string "author"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
create_table "word_lists", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "words", force: :cascade do |t|
t.string "title"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "word_list_id"
end
add_index "words", ["word_list_id"], name: "index_words_on_word_list_id"
end
routes.rb:
Rails.application.routes.draw do
resources :quotes
resources :categories
resources :words
devise_for :users, controllers: { registrations: "users/registrations" }
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
root 'pages#home'
post 'create_word_and_category' => 'new_word#create_word_and_category'
end
The discussion might not be active anymore but I'll share my answer for the future visitors.
Adding "{ checked: #array.map(&:to_param) }" option as the last argument of collection_check_boxes may resolve your problem. Refer this link.
Example:
Suppose you have Software model and Platform(OS) model and want to choose one or more OS that support your software.
#views/softwares/edit.html.erb
<%= form_for #software do |f| %>
...
<%= f.label :supported_platform %>
<%= f.collection_check_boxes(:platform_ids, #platforms, :id, :platform_name, { checked: #software.platform_ids.map(&:to_param) }) %>
...
<%= f.submit "Save", class: 'btn btn-success' %>
<% end %>
note:
#software.platform_ids should be an array. If you are using SQLite, you have to convert string into array when you pull the data out. I tested it with SQLite and confirmed working as well. See my post for more detail.
This should be closer to what you were looking for:
<%= b.label(class: "checkbox-inline", :"data-value" => b.value) { b.check_box + b.text } %>
Try changing your _edit_word_form.html.erb like this
<%= form_for(#word) do |f| %>
<div class="field form-group">
<%= f.label(:title, "Word:") %><br>
<%= f.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %>
</div>
<div class="field form-group">
<%= f.label(:description, "Definition:") %><br>
<%= f.text_area(:description, class: "form-control") %>
</div>
<%= f.fields_for :category do |category_form| %>
<% if current_user.word_list.categories.count > 0 %>
<div class="field form-group">
<%= category_form.label(:title, "Choose from existing Categories:") %><br>
<%= category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) do |b| %>
<%= b.label(class: "checkbox-inline") { b.check_box + b.text } %>
<% end %>
</div>
<% end %>
<h4>AND/OR...</h4>
<div class="field form-group">
<%= category_form.label(:title, "Create and Use a New Category:") %><br>
<%= category_form.text_field(:title, class: "form-control") %>
</div>
<% end %>
<div class="actions">
<%= f.submit("Update!", class: "btn btn-block btn-primary btn-lg") %>
</div>
<% end %>
So user is editing a word from their word list, but you want to show checkboxes for all categories for all the words in their word list, checking those categories attached to the word being editing. Is that correct?
It looks like you're missing out the first parameter in #collection_check_boxes, which should be the object you call :category_ids on.
If the object is the user's word_list, then something like:
<%= category_form.collection_check_boxes(current_user.word_list, :category_ids, current_user.word_list.categories.all, :id, :title) do |b| %>
May not be the exact answer--I can't test it--but hope it gives you something to go on.
What's wrong
When you are calling category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) within the fields_for :category, you are saying there is a method on #word.category called category_ids which will return the ids of the categories related to #word.category. The checkbox will be checked if there is a matching id both in category_ids results and in current_user.word_list.categories.all.
I don't think there is a #word.category or a #word.category.category_ids. I think there's #word.categories though.
How to fix it
That being said, my instinct tells me that you need to use something like:
<%= form_for(#word) do |f| %>
<div class="field form-group">
<%= f.label(:title, "Word:") %><br>
<%= f.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %>
</div>
<div class="field form-group">
<%= f.label(:description, "Definition:") %><br>
<%= f.text_area(:description, class: "form-control") %>
</div>
<% if current_user.word_list.categories.count > 0 %>
<div class="field form-group">
<%= f.label(:categories, "Choose from existing Categories:") %><br>
<%= f.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) %>
</div>
<% end %>
<div class="actions">
<%= f.submit("Update!", class: "btn btn-block btn-primary btn-lg") %>
</div>
<% end %>
Note that I've moved the collection_check_boxes to the #word's form builder, as you are hoping to build the "categories" value for the current #word. I think this should be a step in the right direction anyway.

Resources