Rails 5 - save rolls back because nested models parent model is not being saved before child model - ruby

Ok folks, Rails 5 has really had its nuances differing from Rails 4. What I have going on is that every time I click the submit button on the form it reloads with the error Profile user must exist and Profile user can't be blank. The form loads fine including the nested models form, but for what ever reason it is failing to save the parent model before attempting to save the child model with the following output to the console:
Puma starting in single mode...
* Version 3.7.0 (ruby 2.2.6-p396), codename: Snowy Sagebrush
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started POST "/users" for 192.168.0.31 at 2017-03-09 18:51:04 -0500
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
ActiveRecord::SchemaMigration Load (0.2ms) SELECT `schema_migrations`.* FROM `schema_migrations`
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JPKO+ppAYqwWS8tWeXhEtbUWynXREu9jYlF0KIlyPgUaabHSzjPZocSxCvr/WEm1r6wAQyT1CvA6hNkZWfPD3Q==", "user"=>{"username"=>"test", "password"=>"[FILTERED]", "user_type_id"=>"1", "profile_attributes"=>{"first_name"=>"123", "middle_name"=>"123", "last_name"=>"123", "email"=>"123#123.com", "phone_number"=>"1234567890", "cell_number"=>"1234567890"}}, "commit"=>"Create User"}
(0.1ms) BEGIN
(0.2ms) ROLLBACK
Rendering users/new.html.erb within layouts/application
Rendered users/_form.html.erb (112.5ms)
Rendered users/new.html.erb within layouts/application (118.7ms)
Completed 200 OK in 834ms (Views: 780.1ms | ActiveRecord: 2.2ms)
I have have had other problems out of this relationship and I am thinking that maybe I need to rebuild the project.
Here is all of the relevant code around this issue:
###############################################################################
### Users Model
###############################################################################
class User < ApplicationRecord
has_one :profile, inverse_of: :user
accepts_nested_attributes_for :profile, allow_destroy: true
end
###############################################################################
### Profile Model
###############################################################################
class Profile < ApplicationRecord
belongs_to :user, inverse_of: :profile
validates_presence_of :user
end
###############################################################################
### Users Controller
###############################################################################
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
#user.build_profile
end
# GET /users/new
def new
#user = User.new
#user.build_profile
end
# GET /users/1/edit
def edit
#user.build_profile
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:username, :password, :user_type_id, profile_attributes: [:id, :user_id, :first_name, :middle_name, :last_name, :phone_number, :cell_number, :email])
end
end
###############################################################################
### Form View
###############################################################################
<%= form_for(#user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
<!--<li><%= debug f %></li>-->
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.text_field :password %>
</div>
<div class="field">
<% if params[:trainer] == "true" %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '2' %>
<% else %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '1' %>
<% end %>
</div>
<h2>Account Profile</h2>
<%= f.fields_for :profile do |profile| %>
<%#= profile.inspect %>
<div>
<%= profile.label :first_name %>
<%= profile.text_field :first_name %>
</div>
<div>
<%= profile.label :middle_name %>
<%= profile.text_field :middle_name %>
</div>
<div>
<%= profile.label :last_name %>
<%= profile.text_field :last_name %>
</div>
<div>
<%= profile.label :email %>
<%= profile.text_field :email %>
</div>
<div>
<%= profile.label :phone_number %>
<%= profile.telephone_field :phone_number %>
</div>
<div>
<%= profile.label :cell_phone %>
<%= profile.telephone_field :cell_number %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<%= debug params %>
<%= debug user %>
<%= debug user.profile %>
<% end %>

Alright I rephrased the question on another question and I finally found the answer to this. So I am pasting my answer from there, in case someone searches for the issue in the same fashion that I was asking the question here.
Ok, I am answering my own question because I know many people are struggling with this and I actually have the answer and not a vague response to the documentation.
First we will just be using a one to one relationship for this example. When you create your relationships you need to make sure that the parent model has the following
inverse_of:
autosave: true
accepts_nested_attributes_for :model, allow_destroy:true
Here is the Users model then I will explain,
class User < ApplicationRecord
has_one :profile, inverse_of: :user, autosave: true
accepts_nested_attributes_for :profile, allow_destroy: true
end
in Rails 5 you need inverse_of: because this tells Rails that there is a relationship through foreign key and that it needs to be set on the nested model when saving your form data. Now if you were to leave autosave: true off from the relationship line you are left with the user_id not saving to the profiles table and just the other columns, unless you have validations off and then it won't error out it will just save it without the user_id. What is going on here is autosave: true is making sure that the user record is saved first so that it has the user_id to store in the nested attributes for the profile model.
That is it in a nutshell why the user_id was not traversing to the child and it was rolling back rather than committing.
Also one last gotcha is there are some posts out there telling you in your controller for the edit route you should add #user.build_profile like I have in my post. DO NOT DO IT THEY ARE DEAD WRONG, after assessing the console output it results in
Started GET "/users/1/edit" for 192.168.0.31 at 2017-03-12 22:38:17 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#edit as HTML
Parameters: {"id"=>"1"}
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Profile Load (0.5ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.5ms) UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2017-03-13 02:38:17' WHERE `profiles`.`id` = 1
(59.5ms) COMMIT
Rendering users/edit.html.erb within layouts/application
Rendered users/_form.html.erb (44.8ms)
Rendered users/edit.html.erb within layouts/application (50.2ms)
Completed 200 OK in 174ms (Views: 98.6ms | ActiveRecord: 61.1ms)
If you look it is rebuilding the profile from scratch and resetting the user_id to null for the record that matches the current user you are editing. So be very careful of this as I have seen tons of posts making this suggestion and it cost me DAYS of research to find a solution!

Rails 6.1
I had a similar issue that was fixed by removing validates_presence_of :user and validates :user_id, presence: true from my profile model. Your model obviously did not have the same validations mine had, but I thought it may be useful to add this answer in case others do.
I did not need to add inverse_of or autosave to either model to get the nested attributes to save correctly.

I came across this question because I had the same problem but only for my rspec tests. I ended up doing rails db:reset RAILS_ENV=test and that solved it. I did not need to add inverse_of: :user or autosave:true to the models.
One theory you could check in a dev or production system is if your seed for the table somehow got off. So then it can't insert the user row because the id is in use and then the profile create fails because the user create failed. In postgresql you could run: SELECT setval('users_id_seq', (SELECT max(id) FROM users}));

I was having a similar issue (did not save with nested attributes).
In the controller, I changed my #user.build_profile to #user.profile.build(params[:profile]) and it solved the issue.

Related

Nested formed by cocoon cannot be saved

Problem
I'm coding user's profile page on Rails.
I added a gem "language-select" for users to choose a language which they're learning. And users might learn several languages, so I added gem "cocoon" as well.
The first language which users choose is successfully saved, but others which were chosen by cocoon cannot be saved. I would like them to be saved and being showed on their profile page.
How I reached this problem
I added cocoon by refering this page and code.README in github
However, "Save" button didn't react anything, so I made the form_for's method "post".
<%= form_for #user, method: :post do |f| %>
<div id='languages'>
<%= f.fields_for :languages do |language| %>
<%= render 'language_fields', :f => language %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add language', f, :languages %>
</div>
</div>
<% end %>
Then "Save" button worked, but an error occured.
ActionController::InvalidAuthenticityToken in UsersController#update
ActionController::InvalidAuthenticityToken
Extracted source (around line #211):
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
end
end
end
Therefore, I added "protect_from_forgery with: :null_session" to application_controller.rb. Then the error disappeared, but the problem which I mentioned above occered. The first language which users choose is saved, but other ones which were chosen by cocoon cannot be saved.
(users#show.html.erb)
<h2><%= #user.language %></h2>
(users#edit.html.erb)
<%= form_tag("/users/#{#user.id}/update", {multipart: true}) do %>
<table>
<p>Languages</p>
<%= select_tag(:language,options_for_select(languages)) %>
<%= render 'users/form' %>
<input type="submit" value="Submit">
</table>
<% end %>
(users#_form.html.erb)
Partial of cocoon.
<%= form_for #user, method: :post do |f| %>
<div id='languages'>
<%= f.fields_for :languages do |language| %>
<%= render 'language_fields', :f => language %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add language', f, :languages %>
</div>
</div>
<% end %>
(users#_language.html.erb)
Partial of cocoon.
<div class='nested-fields'>
<div class="field">
<%= select_tag(:language,options_for_select(languages)) %>
</div>
<%= link_to_remove_association "remove language", f %>
</div>
(users_controller.rb)
Even though I settled "redirect_to("/users/#{#user.id}")" here, it redirects to "/posts/index" after press "Save" button. And the sentence "You're already logged in" which I settled at "forbid_login_user" in "application_controller.rb" is shown too.
before_action :authenticate_user, {only: [:index, :show, :edit, :update]}
before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}
before_action :ensure_correct_user, {only: [:edit, :update]}
def edit
#user = User.find_by(id: params[:id])
end
def user_params
params.require(:user).permit(:name, :description, languages_attributes: [:id, :description, :done, :_destroy])
end
def update
#user = User.find_by(id: params[:id])
#user.language = params[:language]
if params[:image]
#user.image_name = "#{#user.id}.jpg"
image = params[:image]
File.binwrite("public/user_images/#{#user.image_name}", image.read)
end
if params[:cover_image]
#user.cover_image_name = "#{#user.id}_cover.jpg"
cover_image = params[:cover_image]
File.binwrite("public/user_cover_images/#{#user.cover_image_name}", cover_image.read)
end
if #user.save
flash[:notice] = "Edited user's information"
redirect_to("/users/#{#user.id}")
else
render("users/edit")
end
end
(application_controller.rb)
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
before_action :set_current_user
def set_current_user
#current_user = User.find_by(id: session[:user_id])
end
def authenticate_user
if #current_user == nil
flash[:notice] = "You need to log in"
redirect_to("/login")
end
end
def forbid_login_user
if #current_user
flash[:notice] = "You're already logged in"
redirect_to("/posts/index")
end
end
end
I will add more code if it needs to be refered. Thank you very much.
Version
ruby 2.6.4p104 / RubyGems 3.0.3 / Rails 5.2.3
Postscript
When I choose only one language, it can be saved.
When I choose like several languages like below, the problem which I mentoined above happens. Nothing is saved.
But after putting "skip_before_action :verify_authenticity_token" in application_controller.rb, only "English"(the last one) is saved (checked by rails console).
"Japanese" (chose by original form)
"English" (added and chose by cocoon)
Anyway, only 1 language can be saved so far.

Beginner stuck on Ruby on Rails project "...must exist"

So my assignment asks me to generate a page that handles students, courses, and sections. I'm supposed to be using many_to_many or some similar method to be able to list all this shared data. My specific problem is that I created a new table (rails g scaffold enrollment students:references sections:references) that doesn't seem to work. when I attempt to create a new enrollment using a student and section, I get an error stating that "Section must exist" error . I have no idea where this error is coming from. The sections field in this view is only populated with existing sections, so the fact that it says "must exist" is very...confusing. Can anyone point me in the right direction to solve this? I've rebuilt this project 3 times trying different methods and am just... stuck. I'll post the relevant code, but if I'm wrong about which sections you may need to see, I'll happily post the rest.
class Enrollment < ApplicationRecord
belongs_to :section
belongs_to :student
end
<%= form_with(model: enrollment, local: true) do |form| %>
<% if enrollment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(enrollment.errors.count, "error") %>
prohibited this enrollment from being saved:</h2>
<ul>
<% enrollment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :student_id %>
<%= form.collection_select :student_id, Student.order(:student_name), :id,
:student_id, include_blank:true %>
</div>
<div class="field">
<%= form.label :course_id %>
<%= form.collection_select :course_id, Course.order(:name), :id, :name,
include_blank: true %>
</div>
<div class="field">
<%= form.label :sections_number %>
<%= form.collection_select :section_number, Section.all, :id,
:section_number, include_blank:false %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
class Section < ApplicationRecord
has_many :enrollments
has_and_belongs_to_many :students, through: :enrollments
belongs_to :course
def numsem
"#{course.name} #{course_id}"
end
end
L
-----------------------EDIT TO ADD CONTROLLER CODE----------------
class EnrollmentsController < ApplicationController
before_action :set_enrollment, only: [:show, :edit, :update, :destroy]
# GET /enrollments
# GET /enrollments.json
def index
#enrollments = Enrollments.all
end
# GET /enrollments/1
# GET /enrollments/1.json
def show
end
# GET /enrollments/new
def new
#enrollment = Enrollments.new
#sections = Section.all
#students = Student.all
end
# GET /enrollments/1/edit
def edit
end
# POST /enrollments
# POST /enrollments.json
def create
#enrollment = Enrollments.new(enrollment_params)
respond_to do |format|
if #enrollment.save
format.html { redirect_to #enrollment, notice: 'Enrollments was
successfully created.' }
format.json { render :show, status: :created, location: #enrollment }
else
format.html { render :new }
format.json { render json: #enrollment.errors, status:
:unprocessable_entity }
end
end
end
# PATCH/PUT /enrollments/1
# PATCH/PUT /enrollments/1.json
def update
respond_to do |format|
if #enrollment.update(enrollment_params)
format.html { redirect_to #enrollment, notice: 'Enrollments was
successfully updated.' }
format.json { render :show, status: :ok, location: #enrollment }
else
format.html { render :edit }
format.json { render json: #enrollment.errors, status:
:unprocessable_entity }
end
end
end
# DELETE /enrollments/1
# DELETE /enrollments/1.json
def destroy
#enrollment.destroy
respond_to do |format|
format.html { redirect_to enrollments_index_url, notice: 'Enrollments was
successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_enrollment
#enrollment = Enrollments.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list
through.
def enrollment_params
params.require(:enrollment).permit(:student_id, :section_id)
end
end
Section must exist
The error is because you are trying to save the #enrollment with a nil section_id which raises that exception. The problem lies with your form where you have this
<%= form.collection_select :section_number, Section.all, :id,
:section_number, include_blank:false %>
which should be
<%= form.collection_select :section_id, Section.all, :id,
:section_number, include_blank:false %>

cant upload images rails and error occur

I want to upload an image to my localhost using gem 'carrierwave', i have painting and galleries controller like below code
painting controller
class PaintingsController < ApplicationController
def index
#paintings=Painting.all
end
def new
#painting=Painting.new
end
def show
#painting=Painting.find(params[:id])
end
def create
#byebug
#painting=Painting.new(painting_params)
if #painting.save
flash[:success]="Created image in album"
redirect_to gallery_path(#painting)
else
flash[:error]="Fail!"
render 'new'
end
end
private
def painting_params
params.require(:painting).permit(:name,:gallery_id)
end
end
Gallery controller
class GalleriesController < ApplicationController
def index
#galleries=Gallery.all
end
def new
#gallery=Gallery.new
end
def show
#gallery=Gallery.find(params[:id])
end
def create
#gallery=Gallery.create!(gallery_params)
redirect_to galleries_path
end
private
def gallery_params
params.require(:gallery).permit(:name)
end
end
ok then 2 model files :
gallery.rb
class Gallery < ApplicationRecord
has_many :paintings
end
painting.rb
class Painting < ApplicationRecord
def access_params
params.require(:painting).permit(:gallery_id, :name, :image)
end
belongs_to :gallery, optional: true
mount_uploader :image, ImageUploader
end
It seems everying goes well but then i stuck at the step showing image on show.html.erb in gallery.
show.html.erb
<div id="paintings">
<% #gallery.paintings.each do |painting| %>
<div class="painting">
<%= image_tag painting.image_url.to_s %>
<div class="name"><%= painting.name %></div>
<div class="actions">
<%= link_to "edit", edit_painting_path(painting) %> |
<%= link_to "remove", painting, :confirm => 'Are you sure?', :method => :delete %>
</div>
</div>
<% end %>
<div class="clear"></div>
</div>
the image isn't showed up althought flash in gallery 's controller reported that i created the image,i inspected the website then i tried print painting s'attributes on show.html.erb
<%= #gallery.name %>
<%= #gallery.paintings.name %>
<%= #gallery.paintings.gallery_id%>
<%= #gallery.paintings.image%>
Only gallery's name and painting's name are printed out. other two methods has an error occur.
undefined method `gallery_id' for #<ActiveRecord::Associations::CollectionProxy []>
I don't know why gallery can only access to painting's name but not others two.I searched for this error but i dont think those situation apply to mine . What is the problem guys?
Your "paintings" is a collection, not a single image, so you need to either iterate on each of them or select the first one:
<%= #gallery.paintings.first.name %>
<%= #gallery.paintings.first.gallery_id %>
<%= #gallery.paintings.first.image %>
I found my error ! in user controller add :image to method painting_params
def painting_params
params.require(:painting).permit(:name,:gallery_id,:image)
end

uninitialized constant error when trying to update the image file

I am trying to use and learn carrierwave. My problem is that I am able to upload files, but when trying to edit them I am getting the error uninitialized constant ImageFile
Heres my images_controller.rb
class ImagesController < ApplicationController
before_action :set_image, only: [:show, :edit, :update, :destroy]
# GET /images
# GET /images.json
def index
#images = Image.all
end
# GET /images/1
# GET /images/1.json
def show
end
# GET /images/new
def new
#image = Image.new
end
# GET /images/1/edit
def edit
end
# POST /images
# POST /images.json
def create
#image = Image.new(image_params)
respond_to do |format|
if #image.save
format.html { redirect_to #image, notice: 'Image was successfully created.' }
format.json { render :show, status: :created, location: #image }
else
format.html { render :new }
format.json { render json: #image.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /images/1
# PATCH/PUT /images/1.json
def update
respond_to do |format|
if #image.update(image_params)
format.html { redirect_to #image, notice: 'Image was successfully updated.' }
format.json { render :show, status: :ok, location: #image }
else
format.html { render :edit }
format.json { render json: image.errors, status: :unprocessable_entity }
end
end
end
# DELETE /images/1
# DELETE /images/1.json
def destroy
#image.destroy
respond_to do |format|
format.html { redirect_to images_url, notice: 'Image was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_image
#image = Image.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def image_params
params.require(:image).permit(:Title, :ImageFile, :Comments)
end
end
and this is the _form.html.erb used in both new and edit
<%= form_for #image, html: { multipart: true } do |f| %>
<% if #image.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#image.errors.count, "error") %> prohibited this image from being saved:</h2>
<ul>
<% #image.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Title %><br>
<%= f.text_field :Title %>
</div>
<div class="field">
<%= f.label :ImageFile %><br>
<%= f.file_field :ImageFile %>
</div>
<div class="field">
<%= f.label :Comments %><br>
<%= f.text_field :Comments %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and here is the image.rb
class Image < ActiveRecord::Base
# attr_accessible :Comments, :ImageFile, :Title
mount_uploader :ImageFile, ImageCreatorUploader
end
please help
You have an error in your Image class. you are not mounting the uploader correctly. You are passing in a symbol of a classname to the mount_uploader method. You need to pass a symbol of the field name. Replace:
class Image < ActiveRecord::Base
mount_uploader :ImageFile, ImageCreatorUploader
end
With:
class Image < ActiveRecord::Base
mount_uploader :image_file, ImageCreatorUploader
end
Method names should not be capitalised. Only class names.
Also, since you are using strong_parameters, you don't need attr_accessible. You've commented the line out, but I figured I would point this out. You've also capitalised your attribute names here, which is not correct. Use snake_case.
In ruby, attribute names, variable names, method names should all start with a small letter. Names starting with Capital letters are considered constants, and class names.
Change all references to your class's attributes (:Title, :ImageFile, :Comments) to snake_case (:title, :image_file, :comments)

Rails - JavaScript view not updating page correctly after Ajax request

I have Questions, which can be voted on. When I list each question on the question's index, I put the "yes" vote and "no" vote buttons alongside each one. I am trying to use Ajax to create votes and render a js view that updates the vote counts.
I'm observing this behavior:
The votes are successfully created and saved. The first "yes" vote made on each question adjusts the count correctly for that question, but creating any "yes" votes after that will not update the count despite the vote objects being saved. The "no" buttons never update the label correctly. Randomly, clicking on a "No" button will increase that question's no_vote count to 80. If I refresh the page, all the counts update to the correct value as expected.
question.rb
class Question < ActiveRecord::Base
attr_accessible :body, :title, :pass_percentage
has_many :votes
has_many :yes_votes, class_name: 'Vote', conditions: { is_yes: true }
has_many :no_votes, class_name: 'Vote', conditions: { is_yes: false }
...
end
vote.rb
class Vote < ActiveRecord::Base
attr_accessible :is_yes, :question_id
belongs_to :question
validates :is_yes, inclusion: { in: [true, false]}
validates :question_id, presence: true
end
views/questions/index.html.erb
...
<%= will_paginate #questions %>
<ul class="questions">
<% #questions.each do |question| %>
<li>
<section class="question_<%= question.id %> clearfix">
<h2><%= link_to question.title, question_path(question) %></h2>
<p><%= truncate(question.body, length: 70) %></p>
<div class="vote-buttons">
<%= render partial: 'shared/yes_vote',
locals: { question: question } %>
<%= render partial: 'shared/no_vote',
locals: { question: question } %>
</div>
</section>
</li>
<% end %>
</ul>
...
_yes_vote.html.erb
<div class="vote-btn yes-vote-btn">
<%= form_for question.votes.build(is_yes: true), remote: true do |f| %>
<%= f.hidden_field :is_yes %>
<%= f.hidden_field :question_id %>
<%= f.submit "Yes", class: "btn btn-large btn-success" %>
<% end %>
<span class="yes-votes-count"><%= question.yes_votes.count %></p>
</div>
_no_vote.html.erb
<div class="vote-btn no-vote-btn">
<%= form_for question.votes.build(is_yes: false), remote: true do |f| %>
<%= f.hidden_field :is_yes %>
<%= f.hidden_field :question_id %>
<%= f.submit "No", class: "btn btn-large btn-danger" %>
<% end %>
<span class="no-votes-count"><%= question.no_votes.count %></span>
</div>
votes_controller.rb
class VotesController < ApplicationController
def new
#vote = Vote.new
end
def create
#vote = Vote.new(params[:vote])
#question = #vote.question
respond_to do |format|
if #vote.save
format.html { redirect_to :back }
format.js
else
format.html { redirect_to :back, flash: { error: "There was an error" } }
format.js
end
end
end
end
questions_controller.rb
class QuestionsController < ApplicationController
def index
#questions = Question.paginate(page: params[:page])
end
...
end
views/vote/create.js.erb
$(".question_<%= #question.id %> .yes-votes-count").html("<%= #question.yes_votes.count %>")
$(".question_<%= #question.id %> .no-votes-count").html("<%= #question.no_votes.count %>")
Server Log:
Started POST "/votes" for 127.0.0.1 at 2013-05-17 02:53:38 -0400
Processing by VotesController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"QD+7gj3G5r68B7cpxxW5ighB5M0Djluxx5XymnG4vog=", "vote"=>{"is_yes"=>"true", "question_id"=>"2"}, "commit"=>"Yes"}
Question Load (0.1ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = 2 LIMIT 1
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "votes" ("created_at", "is_yes", "question_id", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?) [["created_at", Fri, 17 May 2013 06:53:38 UTC +00:00], ["is_yes", true], ["question_id", 2], ["updated_at", Fri, 17 May 2013 06:53:38 UTC +00:00], ["user_id", nil]]
(0.1ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."question_id" = 2 AND "votes"."is_yes" = 't'
(0.0ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."question_id" = 2
Question Exists (0.0ms) SELECT 1 AS one FROM "questions" WHERE ("questions"."title" = 'Crust or no crust?' AND "questions"."id" != 2) LIMIT 1
(31.9ms) commit transaction
CACHE (0.0ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."question_id" = 2 AND "votes"."is_yes" = 't'
(0.1ms) SELECT COUNT(*) FROM "votes" WHERE "votes"."question_id" = 2 AND "votes"."is_yes" = 'f'
Rendered votes/create.js.erb (1.2ms)
Completed 200 OK in 39ms (Views: 2.3ms | ActiveRecord: 32.6ms)
Sorry for the large amount of code. I tried to make it as clear as possible.
Why is this not working? Is there a much better way to get write create.js.erb view?
You wrote two #vote.save condition.Remove the first one and check again.
Must be validation problems as if you are saving same entry two times
Figure out my issue. The problem was in my create.js.erb. I needed to be using the .text() function not .html() to replace the contents of the element.
New code:
$(".question_<%= #question.id %> .yes-votes-count").text("<%= #question.yes_votes.count %>")
$(".question_<%= #question.id %> .no-votes-count").text("<%= #question.no_votes.count %>")
If anyone sees ways to improve my code in general though, post an answer and I'll accept it.

Resources