Rails creating a new object instead of updating it - ruby-on-rails

I'm having trouble with editing an object from the admin panel on my app. (It's a political related app, hence the references to Liberal and Conservative, etc).
When I edit a category it creates a new category (with the same name) instead of updating it. I can't figure out why.
I can see from the log that when I edit the category Rails appears to go to the POST route (POST /admin/categories(.:format) admin/categories#create) instead of the PUT or PATCH route (PATCH /admin/categories/:id(.:format) admin/categories#update)
Apologies if I am using incorrect terminology, I'm not well versed in Rails.
categories_controller.rb:
class Admin::CategoriesController < Admin::DashboardController
before_action :find_category, only: [ :edit, :update, :destroy ]
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
flash[:notice] = 'Category created'
redirect_to admin_categories_path
else
flash[:error] = 'Something was wrong'
render :new
end
end
def edit
end
def update
#category.update(category_params)
if #article.save
flash[:notice] = 'Category saved'
redirect_to admin_categories_path
else
flash[:error] = 'Something was wrong'
render :edit
end
end
def destroy
#category.destroy
redirect_to admin_categories_path
end
private
def find_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:name, :liberal_image, :conservative_image)
end
end
edit.html.slim inside the admin view (using slim):
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
.container
= link_to 'Manage Categories', admin_categories_path
h1 = "Edit Category - #{#category.name}"
= render partial: 'form', locals: { category: #category }
= simple_form_for #category, url: admin_category_path, method: 'delete' do |f|
= f.submit 'Delete category', class: 'btn btn-danger btn-lg'
I'm presuming it's something wrong in one of these files but let me know if there's something else I need to copy up there. Thanks !
_form.html.slim
= simple_form_for #category, url: admin_categories_path, method: 'post' do |f|
div.form-group.required
= f.input :name, label: 'Category Name'
div.form-group.required
= f.input :liberal_image, as: :file, label: 'Liberal Image'
div.target
div.form-group.required
= f.input :conservative_image, as: :file, label: 'Conservative Image'
div.target
div.form-actions
= f.submit 'Save', class: 'btn'
javascript:
$(document).ready(function() {
$('input[type="file"]').on('change', function(event) {
var files = event.target.files;
var image = files[0]
var reader = new FileReader();
reader.onload = function(file) {
var img = new Image();
img.src = file.target.result;
$(event.target).parent().parent().find('.target').html(img);
}
reader.readAsDataURL(image);
console.log(files);
});
});

We'd need to see your form partial to be clear, but from first glance, you have an issue with your update action:
def update
if #category.update(category_params)
redirect_to admin_categories_path, notice: "Category saved"
else
render :edit, error: "Something was wrong"
end
end
You had #article.update - I don't know why, because you haven't even set #article at all.
--
Update
It seems that your form is the cause of the issue:
= simple_form_for #category, url: admin_categories_path, method: 'post' do |f|
Here, you're sending each request to the create action.
You need to use the following:
= simple_form_for [:admin, #category] do |f|

As someone noticed, you are using #article variable that seems to be undeclared. This should cause an ApplicionError, so I think this maybe a simple bug, that your "edit" link actually leads to the "new" action. Does the name field actually contains a category name, or is it empty?
UPDATE: Actually, using #article will not cause an error. But check your links to be sure anyway.

Related

RoR - Form only works once

I'm building a forum app where users can create posts and comment them, at this point the post functionally works fine but the comment form only works once and from then on the post page crashes showing the following error:
NoMethodError in Post#show
undefined method `to_key' for #
Did you mean? to_set
to_ary
through rails console i can see that the comment was saved.
here is the terminal error:
ActionView::Template::Error (undefined method `to_key' for #<Comment::ActiveRecord_Relation:0x007fb845e9cca8>
Did you mean? to_set
to_ary):
7: <h3>Reply to thread</h3>
8:
9: <div class="comments_form">
10: <%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
11: <%= f.label :title %><br>
12: <%= f.text_field :title, placeholder: "Type the comment title here" %><br>
13: <%= f.label :content %><br>
app/views/post/show.html.erb:10:in `_app_views_post_show_html_erb__4054771198415677123_70214711817160'
here is the form:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title, placeholder: "Type the comment title here" %><br>
<%= f.label :content %><br>
<%= f.text_area :content, placeholder: "Type the comment content here" %><br>
<%= f.submit %>
<% end %>
rails mention that error is in this line of the form:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
the comment_controller.rb:
class CommentController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#comments = Comment.all
end
def new
#comment = Comment.new
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment))
#comment.user = current_user
#comment.assign_attributes(comment_params)
if #comment.save
format.html { redirect_to #link, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to :back, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:post_id, :content, :title, :user_id)
end
end
the routes.rb:
devise_for :users
root to: 'post#index'
#Posts
get '/posts/new', to: 'post#new'
get '/post/:id', to: 'post#show', as: 'show'
get '/post/:id/edit', to: 'post#edit', as: 'edit'
post '/post', to: 'post#create'
put '/post/:id', to: 'post#update', as: 'update'
delete '/post/:id', to: 'post#destroy', as: 'delete'
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
and the post_controller:
class PostController < ApplicationController
def index
if params[:user].blank?
#posts = Post.all.order("created_at DESC")
else
posts = Post.where(user_id: #user_id).order("created_at DESC")
end
end
before_action :authenticate_user!
before_action :find_post, only: [:show, :edit, :update, :destroy]
def show
if Comment.where(post_id: #post.id).present?
#comment = Comment.where(post_id: #post.id)
else
#comment = Comment.new
end
end
def new
#post = Post.new
end
def create
#post = Post.new
#post.user_id = current_user.id.to_i
#Post.create(title: title.string, content: content.string, user_id: current_user.id)
#post.assign_attributes(post_params)
if #post.save
flash[:notice] = "Successfully created post!"
#Post.create(title: title.string, content: content.string, user_id: current_user.id)
redirect_to show_path(#post)
else
flash[:alert] = "Error creating new post!"
render :new
end
end
def edit
end
def update
#post.assign_attributes(post_params)
if #post.changed?
#post.save
redirect_to show_path(#post)
else
render edit_path(#post)
end
end
def destroy
#post.destroy
redirect_to root_path#, notice: “Post destroyed”
end
private
def post_params
params.require(:post).permit(:title, :content, :user_id)
end
def find_post
#post = Post.find(params[:id])
end
end
Thanks in advance for your help.
I think replacing this line:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
with this:
<%= form_for #post.comments.new, url: new_comments_path(#post), method: :post, remote: true do |f| %>
Should fix the issue!

Adding an item to a list using ajax and rails

So, I have a Ruby on Rails application where the user have a list of ingredients. When the user adds a new item to the list of ingredients I need to refresh the page so the ingredient are shown in the list. I want this to be done dynamically using Ajax, I looked up to the internet for solutions but none worked :/
Here is my HTML code(index.html.erb):
<%= form_for(:ingredientList, :url => {:controller => 'ingredients', :action => 'index'}, :remote => true) do |f| %>
<div class ="modal-text col-md-6">
<div class="model-subtitle">Ingrediente: </div>
<%= f.select :value, options_for_select(#ingredient.map{ |ing| [ing.name, ing.id]}, html_options = { class: 'form-control', required: true })%>
</div>
<div class="modal-text col-md-6">
<div class="model-subtitle">Quantidade: </div>
<%= f.text_field :quantity, class: 'form-control', placeholder: "quantidade", autofocus: true %>
</div>
<%= f.submit :Salvar, class: 'btn btn btn-success center-block'%>
My controller:
def index
#user = User.find_by id: session[:user_id]
#ingredients = Ingredient.joins(:ingredients_users).where("user_id = ?", session[:user_id]).select("*")
#user_id = session[:user_id]
#ingredient = Ingredient.all
respond_to do |format|
format.js
format.html
if params[:ingredientList]
#userIngredient = IngredientsUser.new
#userIngredient.ingredient_id = params[:ingredientList][:value]
#userIngredient.quantity = params[:ingredientList][:quantity]
#userIngredient.user_id = #user.id
if #userIngredient.save
flash[:success] = "Ingredient saved!"
else
#userIngredient.errors.full_messages.each do |error|
flash[:alert] = error
end
end
end
end
end
I don't know if I make something wrong(probably as is not working) or if I forgot to add something. Do any of you know how to solve this?
I say this without knowing what your app/views/ingredients/index.js file looks like, but I think you have it almost correct.
As soon as you call format.js and format.html in your controller, it will go ahead and render the corresponding view (eg: app/views/ingredients/index.js). Any code that comes after format.js and format.html will be executed only AFTER the rendering is complete.
So in your case, what happens is that the rendering is done prior to actually processing the input (initializing the ingredient, trying to save it, ...). Try moving format.js and format.html to the end of the action code:
def index
#user = User.find_by id: session[:user_id]
#ingredients = Ingredient.joins(:ingredients_users).where("user_id = ?", session[:user_id]).select("*")
#user_id = session[:user_id]
#ingredient = Ingredient.all
respond_to do |format|
if params[:ingredientList]
#userIngredient = IngredientsUser.new
#userIngredient.ingredient_id = params[:ingredientList][:value]
#userIngredient.quantity = params[:ingredientList][:quantity]
#userIngredient.user_id = #user.id
if #userIngredient.save
flash[:success] = "Ingredient saved!"
else
#userIngredient.errors.full_messages.each do |error|
flash[:alert] = error
end
end
end
format.js
format.html
end
end
All of that said, you're "violating" the Rails conventions. An index action should primarily be used to get a series of objects. Saving data should be either via the create (if it's a new record) or update action (if it's a change to an existing record).

Ruby on Rails Partial Locals

On my site users can both take and retake a quiz, meaning I will use the quiz code in a partial view that will be used both by the the edit and new views.
To render my partials I have in my new view:
<%= render partial: "quiz", locals: { url: quiz_bs_path } %>
and in my edit view:
<%= render partial: "quiz", locals: { url: edit_quiz_b_path } %>
The link_to locations are from my rake routes results:
quiz_bs GET /quiz_bs(.:format) quiz_bs#index
POST /quiz_bs(.:format) quiz_bs#create
new_quiz_b GET /quiz_bs/new(.:format) quiz_bs#new
edit_quiz_b GET /quiz_bs/:id/edit(.:format) quiz_bs#edit
quiz_b GET /quiz_bs/:id(.:format) quiz_bs#show
PATCH /quiz_bs/:id(.:format) quiz_bs#update
PUT /quiz_bs/:id(.:format) quiz_bs#update
DELETE /quiz_bs/:id(.:format) quiz_bs#destroy
I am getting an Argument Error in my edit view saying First argument in form cannot contain nil or be empty. The line on which the error is being called is in my partial:
<%= form_for #quiz_bs, url: url, method: :post do |f| %>
My controller code is:
class QuizBsController < ApplicationController
before_action :require_sign_in
def show
#quiz_bs = QuizBs.find(params[:id])
end
def new
#quiz_bs = current_user.quiz_bs || current_user.build_quiz_bs
end
def create
#quiz_bs = QuizBs.new
#quiz_bs.bs01 = params[:quiz_bs][:bs01]
#quiz_bs.bs02 = params[:quiz_bs][:bs02]
#quiz_bs.bs03 = params[:quiz_bs][:bs03]
#quiz_bs.bs04 = params[:quiz_bs][:bs04]
#quiz_bs.bs05 = params[:quiz_bs][:bs05]
#quiz_bs.bs06 = params[:quiz_bs][:bs06]
#quiz_bs.user = current_user
if #quiz_bs.save
flash[:notice] = "Quiz results saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your quiz results failed to save."
redirect_to welcome_index_path
end
end
def update
#quiz_bs = QuizBs.find(params[:quiz_bs])
#quiz_bs.assign_attributes(quiz_bs_params)
if #quiz_bs.save
flash[:notice] = "Post was updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving the post. Please try again."
redirect_to welcome_index_path
end
end
private
def quiz_bs_params
params.require(:quiz_bs).permit(:bs01, :bs02, :bs03, :bs04, :bs05, :bs06)
end
end
The model is:
class QuizBs < ActiveRecord::Base
before_save :set_bscode
def set_bscode
self.bscode = "#{self.bs01}#{self.bs02}#{self.bs03}-#{self.bs04}#{self.bs05}#{self.bs06}"
end
belongs_to :user
validates :user, presence: true
end
Any ideas where I am going wrong?
Keep it more simple, if you want to assign to current_user, create method will do for you.
Partial:
#new.html.erb
<%= render partial: "quiz", locals: {quiz: #quiz_bs, url: :quiz_bs } %>
#edit.html.erb
<%= render partial: "quiz", locals: {quiz: #quiz_bs, url: :quiz_b } %>
Form:
#_form.html.erb
<%= form_for quiz, url: url do |f| %>
Controller:
def new
#quiz_bs = QuisBs.new
end
You do not have edit action in controller, so rails assumes it being empty. Thus #quiz_bs is really nil
Add something like
def edit
#quiz_bs = QuizBs.find(params[:id])
end
also in your update action params[:quiz_bs] is most likely nil too and should be changed to params[:id]
It is not necessary to create RESTfull methods in controller but if you are using it as I can see #quiz_bs then you will have to initialize it first then you can use it, In your new view you initialize the #quiz_bs by
#quiz_bs = current_user.quiz_bs || current_user.build_quiz_bs
then you are using it.
and also routes which is generated for edit is needed an id i.e.
edit_quiz_b GET /quiz_bs/:id/edit(.:format) quiz_bs#edit
so you will have to pass #quiz_bs like
edit_quiz_b_path(#quiz_bs)
GOT it?
Some updates to your controller:
before_action :assign_quiz_bs, only: [:show, :edit, :update, :destroy]
#...
def update
#quiz_bs.update(quiz_bs_params)
# .... rendering ...
end
private
def assign_quiz_bs
#quiz_bs = Quiz.find(params[:id])
end
def quiz_bs_params
params.require(:quiz_bs).permit(:bs01, :bs02, :bs03, :bs04, :bs05, :bs06)
end

Rails - what's the correct path for inner controllers?

I have this setup in the routes.rb:
resources :users do
resources :images do
resources :comments
end
end
When I load the show action from the ImagesController, this is what is in the template file:
...
= render 'comments/form', :#comment => #image.comments.new
...
and in the comments/form file is following:
= form_for [#comment.commentable, #comment] do |f|
.field
= f.text_field :body
.actions
= f.submit 'Comment'
And the show action:
def show
#user = User.find(params[:user_id])
#image = #user.images.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #image }
end
end
But when I load this page in the browser, I get this error:
undefined method `image_comments_path' for #<#<Class:0x007feb48488128>:0x007feb48399668>
Why Rails returns this error message?
Open your terminal and type rake routes to see the right routes available in your app
In your show method,
def show
#user = User.find(params[:user_id])
#image = #user.images.find(params[:id])
## Add this line:
#comment = #image.comments.build
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #image }
end
end
In your template, if you are rendering a template,
= render "comments/form", :locals => { :comment => #comment } %>
In your comments/form write like this:
= form_for comment do |f|
.field
= f.text_field :body
.actions
= f.submit 'Comment'
It should work. Thanks

How to use form partial for new and edit action with admin routes?

How should I setup url for that partial form to be able use it for create and update action.
Right now I have to set up two urls: admin_faculty_path for update and admin_faculties_path for create action.
# admin/faculties/_form.html.haml
= simple_form_for #faculty, html: { multipart: true, class: 'form-horizontal' } do |f|
= f.input :department_id, as: :hidden, input_html: { value: params[:department_id] } if params[:department_id]
= f.input :name
= f.input :name_en
= f.submit
Routes:
admin_faculties POST /admin/faculties(.:format) admin/faculties#create
new_admin_faculty GET /admin/faculties/new(.:format) admin/faculties#new
edit_admin_faculty GET /admin/faculties/:id/edit(.:format) admin/faculties#edit
admin_faculty GET /admin/faculties/:id(.:format) admin/faculties#show
PUT /admin/faculties/:id(.:format) admin/faculties#update
Controller:
# faculties_controller.rb
def new
#faculty = Faculty.new
end
def create
#faculty = Faculty.new(params[:faculty])
if #faculty.save
redirect_to admin_departments_path,
notice: t('activerecord.successful_save_data')
else
flash[:error] = t('activerecord.save_data_error_html')
render 'new'
end
end
def edit
#faculty = Faculty.find(params[:id])
end
def update
#faculty = Faculty.find(params[:id])
if #faculty.update_attributes(params[:faculty])
redirect_to admin_departments_path,
notice: t('activerecord.successful_save_data')
else
flash[:error] = t('activerecord.save_data_error_html')
render 'edit'
end
end
You need to pass the namespace to the form:
simple_form_for [:admin, #faculty]
See for example
Rails Routes Namespaces and form_for

Resources