Rails: model validations and errors not appearing - ruby-on-rails

I'm creating multiple objects from a single form:
def new
#opening_hour = []
7.times do
#opening_hour << OpeningHour.new
end
render :template => "stores/opening_hours/new"
end
def create
params["opening_hour = "].each do |hour|
if hour["day"] != "" || hour["closes"] != "" || hour["opens"] != ""
#res = OpeningHour.new(opening_hour_params(hour))
puts #res.errors unless #res.save
redirect_to(store_items_index_path)
return
end
end
end
and the form looks like this:
<%= form_tag store_opening_hours_create_path do %>
<% #opening_hour.each do |hour| %>
<%= fields_for 'opening_hour = []', hour do |p|%>
<% if #res.errors.any? %>
<div class="centerList">
<div id="error_explanation">
<h2><%= pluralize(#res.errors.count, "error") %> <%= t 'store_edit_account_errors' %></h2>
<% #res.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</div>
</div>
<% end %>
But after I click on submit... I get an error: undefined method 'errors' for nil:NilClass at this line <% if #res.errors.any? %>. If I delete the error part from the form then everything works fine and the all 7 objects get created. From my understanding this is happening because the value #res is nil... but not really sure how to make this work... any ideas??
Update 1
def new
#opening_hour = []
7.times do
#opening_hour << OpeningHour.new
end
render :template => "stores/opening_hours/new"
end
def create
params["opening_hour = "].each do |hour|
if hour["day"] != "" || hour["closes"] != "" || hour["opens"] != ""
#res = OpeningHour.new(opening_hour_params(hour))
if #res.success
format.html { redirect_to #res, notice: 'Hours were successfully created.' }
format.json { render :show, status: :ok, location: #res }
else
format.html { render :'stores/opening_hours/edit' }
format.json { render json: #res.errors, status: :unprocessable_entity }
end
end
end
end

Try instantiating it at the top of the create action. Probably it's not getting set as you think it is.
def create
#res = OpeningHour.new
# ...
end
You should also have to do something similar in the new action, #res is also not set there.

If you redirect, you trigger a new request where everything that happens before in memory is not persisted.
This is the reason why:
on success we redirect: everything went fine
on error, we render the form directly so it has the object with errors to display

Related

undefined method `errors' for #<Comment cannot get errors

So I'm getting the following error:
undefined method `errors' for #<Comment::ActiveRecord_Associations_CollectionProxy:0x00007f1a3e2ecf48>
I can get the comment count and other things like that, however, I can't display any errors of the validations.
Here is what I have in terms of my code
_new_comment
<% if signed_in? %>
<div class="row">
<%= form_with(model: [#product, #product.comments.build], local: true) do |f| %>
<% if #product.comments.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.comments.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #product.comments.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
comments_controller
class CommentsController < ApplicationController
def create
#product = Product.find(params[:product_id])
#comment = #product.comments.new(comment_params)
#comment.user = current_user
#comment.save
respond_to do |format|
if #comment.save
format.html { redirect_to #product, notice: 'Review was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { redirect_to #product, alert: 'Review was not saved successfully.' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
end
private
def comment_params
params.require(:comment).permit(:user_id, :body, :rating)
end
end
Any help here is appreciated.
Issues
You can not access errors on an ActiveRecord::Relation object, but on individual Comment objects.
In your controller action create, you do redirect_to(#product) on failing to save the comment, which will ignore already built #comment object and build a new one (through form_with(model: [#product, #product.comments.build])) when rendering the comment form. Instead, you need to only render the product page with already built #comment object.
You have #comment.save twice in your create action.
Solution
products_controller.rb
def show
# your current logic
#comment = #product.comments.build
# render
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#product = Product.find(params[:product_id])
#comment = #product.comments.new(comment_params)
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #product, notice: 'Review was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render 'products/show', alert: 'Review was not saved successfully.' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
private
def comment_params
params.require(:comment).permit(:user_id, :body, :rating)
end
end
_new_comment.html.erb
<%= form_with(model: [#product, #comment], local: true) do |f| %>
<% if #comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #comment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>

rails - if condition on view - undefined method `id' for nil:NilClass

I have a class called #Subdomain:
Here is my controller:
class SubdomainsController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
before_action :set_subdomain, only: [:show, :edit, :update, :destroy]
before_action :redirect_to_subdomain, only: :show
before_action :redirect_to_subdomain_show, only: :root, unless: '#subdomain.nil?'
def root
render nothing: true
end
def index
#subdomains = Subdomain.all
end
def show
end
def new
#subdomain = Subdomain.new
end
def edit
end
def create
#subdomain = Subdomain.new(subdomain_params)
respond_to do |format|
if #subdomain.save
format.html { redirect_to #subdomain, notice: 'Subdomain was successfully created.' }
format.json { render :show, status: :created, location: #subdomain }
else
format.html { render :new }
format.json { render json: #subdomain.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #subdomain.update(subdomain_params)
format.html { redirect_to #subdomain, notice: 'Subdomain was successfully updated.' }
format.json { render :show, status: :ok, location: #subdomain }
else
format.html { render :edit }
format.json { render json: #subdomain.errors, status: :unprocessable_entity }
end
end
end
def destroy
#subdomain.destroy
respond_to do |format|
format.html { redirect_to subdomains_url, notice: 'Subdomain was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def redirect_to_subdomain_show
redirect_to subdomain_url(#subdomain) if #request_subdomain.eql? #subdomain.address
end
# Redirect to subdomain... if the current one isn't equal to #subdomain.address column
def redirect_to_subdomain
redirect_to subdomain_url(#subdomain, subdomain: #subdomain.address) unless #request_subdomain.eql? #subdomain.address
end
def set_subdomain
#subdomain = Subdomain.find(params[:id])
end
def subdomain_params
#subdomain.assign_attributes(tag_speciality: params[:subdomain][:tag_speciality].split(','))
params.require(:subdomain).permit(
:domain_id,
:address,
:title,
:description,
:is_published,
:button_text_client,
:button_text_service,
:cover,
:primary_colour,
:secundary_colour,
:contrast_colour,
:logo,
:find_clients,
:find_professional,
:start_relation,
:publications_wall,
:tag_speciality,
:cards_wall
)
end
end
On the view i created a condition:
<% if (!#subdomain.id.blank?) %>
<li>
<%= link_to cards_from_subdomain_path(subdomain_id: #subdomain.id) do %>
<i class="fa fa-search fa-lg"></i>
<span class="hidden-medium">
&nbsp <%= #subdomain.cards_wall %>
</span>
<% end %>
</li>
<li>
<%= link_to publications_from_subdomain_path(subdomain_id: #subdomain.id) do %>
<i class="icon-grid fa-lg"></i>
<span class="hidden-medium">
&nbsp <%= #subdomain.publications_wall %>
</span>
<% end %>
</li>
<% else %>
<li>Some thing</li>
<% end %>
Here is the model:
class Subdomain < ActiveRecord::Base
belongs_to :domain
has_many :cards
mount_uploader :cover, AssetsUploader
mount_uploader :logo, AssetsUploader
end
When the #subdomain exist is ok, everything works fine, but when it doesn't exist, i get this error message:
NoMethodError in Subdomains#index
undefined method `id' for nil:NilClass
<% if (!#subdomain.id.blank?) %>
What could i do to fix that? thanks
The quick and dirty solution would be to use #try (not recommended though)
<% if !#subdomain.try(:id).try(:blank?) %>
By the way, you can use #present? instead of negating a #blank? to make code more readable.
However, if I assume correctly and the #subdomain is an ActiveRecord model, you don't need to check if it's id is present. A following code should be sufficient in your case:
<% if #subdomain %>
(...)
<% else %>
(...)
<% end %>
If the #subdomain is not found, it will be a nil anyway - and then the if #subdomain condition will be evaluated as false - since nil is a falsy value.
And if you get this error because you happen to be rendering a collection like this:
= render 'subdomain', collection: #subdomains
...then you can just put that inside a condition...
<% if #subdomains.present? %>
= render 'subdomain', collection: #subdomains
<% else %>
<% # #subdomains are an empty collection %>
<% end %>
From your create action, id will definitely be created in your table. I don't still understand what you are trying to get to, for negating on #id.blank?.
Change the first line from your view. i.e.
<% if (!#subdomain.id.blank?) %>
to,
<% if #subdomains %>, which will definitely return all attributes on your Subdomain Table since its has being taken care of in your index action.
Another thing I do is wrap my if...else statement inside a loop, such the your view becomes:
<% #subdomains.each do |subd| %>
<% if subd.id.present? %> #you can use another attribute here instead of id. e.g. <% if subd.is_published == true %> since `is_published` is a boolean.
<li>
(#your codes here referenced with the format called `subd.whatever_attribute`)
</li>
<li>
(#your codes here referenced with the format called `subd.whatever_attribute`)
</li>
<% else %>
<li>Some thing</li>
<% end %>
<% end %>

Rails: How not to save value with using time_select

I'm trying to add time_select with include_blank. I'm doing this:
<%= f.time_select :start_at, include_blank: true, ampm: true %><br>
What I'd like to do is to delete value (save nil?) if blank is selected in view.
Although I tried the following posts, it didn't work for me.
time_select blank field saves a default time when form is submitted
Optional time_select with allow_blank defaults to 00:00
1) When I try as below, no error is appeared, but 00:00:00 is saved.
controller
def update
#event = Event.find(params[:id])
if event_params["start_at(4i)"].blank? or event_params["start_at(5i)"].blank?
#event.start_at = nil
end
if #event.update(event_params)
flash[:success] = "event updated!"
redirect_to root_url
else
render 'edit'
end
end
2) When I try as below (change if clause), no error is appeared, but 00:00:00 is saved.
controller
def update
#event = Event.find(params[:id])
if params[:id]["start_at(4i)"].blank? or params[:id]["start_at(5i)"].blank?
#event.start_at = nil
end
if #event.update(event_params)
flash[:success] = "event updated!"
redirect_to root_url
else
render 'edit'
end
end
3) When I try as below (add before_action), no error is appeared, but 00:00:00 is saved.
controller
before_action :blank_time, only: [:update]
def update
#event = Event.find(params[:id])
if #event.update(event_params)
flash[:success] = "event updated!"
redirect_to root_url
else
render 'edit'
end
end
private
def blank_time
if params[:id]["start_at(4i)"].blank? or params[:id]["start_at(5i)"].blank?
params[:id]['start_at(1i)'] = ""
params[:id]["start_at(2i)"] = ""
params[:id]["start_at(3i)"] = ""
params[:id]["start_at(4i)"] = ""
params[:id]["start_at(5i)"] = ""
end
end
4) When I try as below (use nil instead of ""), error is appeared.
error
IndexError (string not matched):
app/controllers/events_controller.rb:106:in `[]='
app/controllers/events_controller.rb:106:in `blank_time'
controller
before_action :blank_time, only: [:update]
def update
#event = Event.find(params[:id])
if #event.update(event_params)
flash[:success] = "event updated!"
redirect_to root_url
else
render 'edit'
end
end
private
def blank_time
if params[:id]["start_at(4i)"].blank? or params[:id]["start_at(5i)"].blank?
params[:id]['start_at(1i)'] = nil
params[:id]["start_at(2i)"] = nil
params[:id]["start_at(3i)"] = nil
params[:id]["start_at(4i)"] = nil
params[:id]["start_at(5i)"] = nil
end
end
It would be appreciated if you could give me any advice.
UPDATE
Although I change the edit in events_controller.rb as below, the error ActiveModel::MissingAttributeError (can't write unknown attribute 'start_at(4i)'): is displayed.
def edit
#room = Room.find(params[:room_id])
#event = #room.events.find(params[:id])
#event['start_at(4i)'] = #event.start_at.split(':')[0] #the error occur here
#event['start_at(5i)'] = #event.start_at.split(':')[1]
end
My idea works like this:
Migration:
class CreateTests < ActiveRecord::Migration[5.0]
def change
create_table :tests do |t|
t.string :time
t.timestamps
end
end
end
Form:
<%= form_for(test) do |f| %>
<% if test.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(test.errors.count, "error") %> prohibited this test from being saved:</h2>
<ul>
<% test.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :time %>
<%= f.time_select :time, include_blank: true, ampm: false %><br>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controller:
This will save : when value send are null where you can use conditional to check if parameters are null or and set value of time. //It's consuming much time and I skipped for you to complete.
def create
#test = Test.new(test_params)
#time = (params[:test]['time(4i)']).to_s
#time_ampm = (params[:test]['time(5i)']).to_s
#test.time = #time+":"+ #time_ampm
respond_to do |format|
if #test.save
format.html { redirect_to #test, notice: 'Test was successfully created.' }
format.json { render :show, status: :created, location: #test }
else
format.html { render :new }
format.json { render json: #test.errors, status: :unprocessable_entity }
end
end
end
For updating
def edit
#test = Test.find(params[:id])
#test['time(4i)'] = #test.time.split(':')[0]
#test['time(5i)'] = #test.time.split(':')[1]
end
def update
#test = Test.find(params[:id])
#time = (params[:test]['time(4i)']).to_s
#time_ampm = (params[:test]['time(5i)']).to_s
#test.time = #time+":"+ #time_ampm
#test.update(test_params)
end
Assigning #event.starts_at to nil does nothing as the attributes in #event_params is used when calling #update, overwriting your initial assignment.
Overwriting the starts_at attribute in your params should work instead.
def update
#event = Event.find(params[:id])
if event_params["start_at(4i)"].blank? or event_params["start_at(5i)"].blank?
event_params = event_params.reject { |k, v| k.starts_with? 'starts_at' }
.merge(starts_at: nil)
end
if #event.update(event_params)
flash[:success] = "event updated!"
redirect_to root_url
else
render 'edit'
end
end
The following line finds and remove the parameters for starts_at(1i) to starts_at(5i), then sets the whole starts_at attribute to be nil:
event_params.reject { |k, v| k.starts_with? 'starts_at' }.merge(starts_at: nil)

undefined method `picture' for nil:NilClass using carrierwave

I am making a form in my rails application where people have the option of adding images and I am using 'carrierwave' but I am getting an undefined method error on the edit page. Here is the code for the form:
<%= title "Add Item to #{#todo_list.title}" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<%= form.file_field :picture %>
<% end %>
Here I can see the upload button and it is working fine but on the edit page I get the above stated error. Code for my edit page:
<%= title "Editing Todo Item" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<% end %>
<div class="row">
<div class="small-12 columns">
<%= link_to "Delete", todo_list_todo_item_path(#todo_list, #todo_item), method: :delete, data: { confirm: "Are you sure?" }, class: "button radius expand alert" %>
</div>
<%= #todo_item.picture %>
</div>
Why is this showing an undefined method error. I tried creating a method in my todo_item model but its still showing the above error.
Controller for todo_item:
class TodoItemsController < ApplicationController
before_action :require_user
before_action :find_todo_list
before_action :set_back_link, except: [:index]
def index
go_back_link_to todo_lists_path
end
def new
#todo_item = #todo_list.todo_items.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_item_params)
if #todo_item.save
flash[:success] = "Added todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "There was a problem adding that todo list item."
render action: :new
end
end
def edit
#todo_item = #todo_list.todo_items.find(params[:id])
end
def update
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.update_attributes(todo_item_params)
flash[:success] = "Saved todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "That todo item could not be saved."
render action: :edit
end
end
def destroy
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.destroy
flash[:success] = "Todo list item was deleted."
else
flash[:error] = "Todo list item could not be deleted."
end
redirect_to todo_list_todo_items_path
end
def complete
#todo_item = #todo_list.todo_items.find(params[:id])
#todo_item.toggle_completion!
redirect_to todo_list_todo_items_path, notice: "Todo item updated."
end
def url_options
{ todo_list_id: params[:todo_list_id] }.merge(super)
end
private
def set_back_link
go_back_link_to todo_list_todo_items_path(#todo_list)
end
def find_todo_list
#todo_list = current_user.todo_lists.find(params[:todo_list_id])
end
def todo_item_params
params[:todo_item].permit(:content)
end
end
To display your image you should change
<%= #todo_item.picture %>
to
<%= image_tag(#todo_item.picture_url) %>

Rails create form for model with many to many relation

I have two models, Recipe and Tag, with a has_and_belongs_to_many relation. For this relation I have a simple join table, RecipesTags.
Recipe:
has_and_belongs_to_many :tags
Tag:
has_and_belongs_to_many :recipes
Now upon creating a new recipe, the user gets to fill in which category the recipe belongs to in forms of checkboxes, like "Meat", "Fish", and so on. These categories are in fact just tags in the database.
Problem: the recipes doesn't get any tags saved to it.
Recipe new and create controller methods:
def new
#recipe = Recipe.new
#ingredients = Ingredient.all
#tags = Tag.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #recipe }
end
end
# POST /recipes
# POST /recipes.json
def create
#recipe = Recipe.new(params[:recipe])
if (params[:tags])
#recipe.tags << params[:tags]
end
respond_to do |format|
if #recipe.save
format.html { redirect_to #recipe, notice: 'Recipe was successfully created.' }
format.json { render json: #recipe, status: :created, location: #recipe }
else
format.html { render action: "new" }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
The view:
<%= form_for(#recipe, :html => {:multipart => true}) do |f| %>
<% if #recipe.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#recipe.errors.count, "error") %> prohibited this recipe from being saved:</h2>
# [ fields that get's saved for the recipe and works fine ]
<% #tags.each do |t| %>
<%= f.label t.name %>
<%= f.check_box :tags, t.name %>
<br />
<% end %>
<%= f.submit 'Submit recipe', :class => 'btn btn-primary' %>
<% end %>
At the moment, I get an error message saying:
undefined method `merge' for "Meat":String
"Meat" is the tag name.
So, what am I doing wrong here?
I think the issue is this line #recipe.tags << params[:tags].
The association method you're calling with << takes an object (in this case expecting a tag object), but in this case it seems you might be passing it a string.
For more info this link may be helpful http://guides.rubyonrails.org/association_basics.html#has_and_belongs_to_many-association-reference, in particular where it refers to collection<<(object, …).
In your controller you'll want to do something like #recipe.tags << tag where tag is a specific tag object.
So, try this:
In your controller
params[:tags].each do |k,v|
#recipe.tags << Tag.find(k)
end
In your view
<% #tags.each do |t| %>
<%= f.label t.name %>
<%= f.check_box "tags[#{t.id}]" %>
<br />
<% end %>
Try this:
def create
#recipe = Recipe.new(params[:recipe])
params[:tags].each do |tag|
#recipe.tags << Tag.find_by_name(tag)
end
respond_to do |format|
if #recipe.save
format.html { redirect_to #recipe, notice: 'Recipe was successfully created.' }
format.json { render json: #recipe, status: :created, location: #recipe }
else
format.html { render action: "new" }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
In view:
<% #tags.each do |t| %>
<%= label_tag t.name %>
<%= check_box_tag "tags[#{t.name}]", t.name %>
<br />
<% end %>

Resources