Rails 3: save method wont work - ruby-on-rails

I'm building my first Rails Application and until now everything went fine but then I found the following scenario: One Presentation is supposed to have N Iterations. I'm NOT using REST. So, I was trying to make a simple form to create iterations.
These are the models:
class Presentation < ActiveRecord::Base
has_many :iterations
end
class Iteration < ActiveRecord::Base
belongs_to :presentation
attr_accessible :presentation_id, :description, :delivery_date, :file
validates :presentation_id, :presence => {:message => 'is required.'}
end
These are the actions in the controller:
#Shows Form
def add
#iteration = Iteration.new
#presentation = Presentation.find(params[:id])
end
#Saves Form
def save
#iteration = Iteration.new(params[:iteration])
#iteration.delivery_date = Time.now
if #iteration.save
flash[:notice] = "Saved succesfully!"
else
flash[:error] = "Changes were not saved."
end
redirect_to root_url
end
These would be the view in HAML:
= form_for #iteration, :url => { :action => "save", :method => "post" }, :html => { :multipart => true } do |f|
- if #iteration.errors.any?
There were some errors:
.notice-text.fg-color-white
%ul.notice
- for message in #iteration.errors.full_messages
%li= message
%br
.field
= f.label :description, "Description"
= f.text_area :description, :class=>"form-text-area", :rows=>5
.field
= f.label :file, "Upload File"
= f.file_field :file
.field
= hidden_field_tag :presentation_id, #presentation.id
%br
= f.submit "Save"
The problem is, save method wont save, but #iteration.errors.count's value on the view is 0.
I used then save! instead as I read in another post, that way it throw the following error:
Validation failed: Presentation is required.
I can't figure out what I'm doing wrong. Please notice that in the view I used to have "f.hidden_field" instead of "hidden_field_tag" but I changed it for some other reasons, however I was getting the same error before that.

Your HAML,
hidden_field_tag :presentation_id
needs to be,
f.hidden_field :presentation_id, :value => #presentation.id

Looking at the your model definition you can have,
Nested resource: Refer to Controller path for nested resource - undefined method `<controller>_path'
Use Virtual attributes: Extremely useful railcasts by Ryan on this -> http://railscasts.com/episodes/16-virtual-attributes-revised
Save the presentation id in session: (This is not a clean very clean method)
On your controller, you will need to instantiate iteration on presentation so that presentation id is correctly populated.

Related

Issues with Simple_form and accepts_nested_attributes_for

I'm quite new with RoR so sorry if I say something not correct.
I have these models.
class Course < ApplicationRecord
has_many :frequencies, inverse_of: :course
belongs_to :subject, optional: true
validates :start_date, presence: true
end
class Frequency < ApplicationRecord
belongs_to :user
belongs_to :course
validates :course, presence: true
validates_presence_of :course
accepts_nested_attributes_for :user, :course
has_many_attached :docs
end
The relationship between Course and Frequency is 1:N but, at the end, I use it as a 1:1 (things changed after defined the models).
This is the view app/views/frequencies/show.html.haml
= simple_form_for #frequency, :url => frequencies_update_path(:id => #frequency.id) do |f|
.panel.panel-primary
.panel-heading
%h4.panel-title= t 'frequencies.upd_frequency'
.panel-body
= f.simple_fields_for :user do |u|
.row
.col-md-4
= u.label t 'frequencies.first_name'
.col-md-4
= u.input :first_name, :label => false, :disabled => true, :input_html => {:id => 'first_name'}
.row
.col-md-4
= u.label t 'frequencies.last_name'
.col-md-4
= u.input :last_name, :label => false, :disabled => true, :input_html => {:id => 'last_name'}
-#= u.hidden_field :id, value: #user_id
.panel.panel-primary
.panel-heading
%h4.panel-title= t 'frequencies.course'
.panel-body
= f.simple_fields_for :course do |u|
.row
.col-md-4
= u.label t 'frequencies.course_start_date'
.col-md-4
= u.input :start_date, :label => false, :disabled => (#frequency.validated? ? true : false), :input_html => {:id => 'course_start_date'}
.
.
.
= f.submit t('button.save'), :class => 'btn btn-primary ' + (current_user.role == $admin_role && #frequency.validated? ? 'disabled' : '')
= link_to t('button.cancel'), request.referer.present? ? request.referer : frequencies_index_path, :class => 'btn btn-default'
This is part of the frequencies_controller.rb
def update
#frequency = Frequency.find params[:id]
#course = Course.find #frequency.course_id
if over_max_hours_in_a_day(frequency_params[:user_attributes][:id], #course)
flash[:danger] = t('flash.max_hours')
render :action => :show and return
end
if #course.update(frequency_params[:course_attributes])
#frequency.docs.attach(frequency_params[:attach][:docs]) if (frequency_params[:attach].present? && frequency_params[:attach][:docs].present?)
flash[:notice] = t('flash.upd')
redirect_to :action => 'index' and return
else
flash[:danger] = #course.errors.full_messages.to_sentence
render :action => :show and return
end
end
def show
#frequency = Frequency.find params[:id]
#subjects = Subject.all
end
I'm able to edit a course from the related frequency's view but I have some strange behaviours:
when I save the validation process occurs but I have the error message only as flash message and not under the involved field (in others simpler views I have the message also under the field)
when I edit some course's fields (from the frequency view) and after I click on the save button it calls the update action but, if it runs inside the over_max_hours_in_a_day if condition, I'm not able to stay on the same view with the modified fields precompiled (but I have the fields like it loads at the beginning show action)
when I press the cancel button after a previous failed edit I remain on the same page instead of come back to the previous view (index view)
I'm not sure if this is due to accepts_nested_attributes_for on a belongs_to model, because I usually see it in a has_many model.
Rails 5 5.2.2
simple_form 4.1.0
Please, can you help me?
Thanks.
separate logic depending on the view (from show / index pages). Create 2 update methods
in case of error just render view again render :show OR render :index
I think it's better move logic of over_max_hours_in_a_day to model
for cancel button don't use referrer because after update it won't work. Pass exact back url using form locals or other way
if you want to hightligh fields - you must call .update, .save method on it with form parameters

Getting nested forms to render

I'm trying to build a nested form, but the nested form of question_fields isn't rendering in the browser. That form has a nested form called answers, also not rendering
Here's the nested form, _createpoll.html.haml
= form_for Poll.new, :class=>'create-poll-form', :remote => true do |f|
= f.text_field :title, :autofocus => true, :placeholder => "Poll Title"
= f.text_field :description, :placeholder => 'Description'
/ Required accepts_nested_attributes_for :questions in the polls model.
= f.fields_for :questions do |builder|
= render "questions/question_fields", :f => builder
= f.submit "Create Poll", :class => 'btn btn-danger'
Here's the _questions_fields.html.haml:
%p
= f.label :content, "Question"
= f.text_area :content
= f.check_box :_destroy
= f.label :_destroy, "Remove Question"
%p
= f.fields_for :answers do |builder|
= render "answers/answer_fields", :f => builder
Here's the related Polls Controller, new and create actions
def create
#poll = Poll.create(params[:poll])
end
def new
#poll = Poll.new
1.times do
question = #poll.questions.build
2.times {question.answers.build}
end
end
Any ideas on why this might not be rendering? Thanks in advance for the tips!!
Update, a new question
After creating the poll with its associated questions and answers, after querying the database, I see that that the foreign keys aren't persisted and the association is lost. Do I have to use hidden fields here somehow?
Silly oversight. Poll.new has to be #poll... whoops.
Updating Answer.
This form was was rendered by a button on the user's dashboard, "Create Poll," routing through the controller's new action.
As controller's new action instantiates the new poll, I was being redundant when instantiating another poll in the form for. By switching this to the newly created instance variable #poll, the form rendered. Also, :content threw a no method error, but that's because it wasn't in the attr_accessible of the questions or answers models.

HAML and mistake in form_for

I'm learning HAML and I have a problem with from_for
%b New Advert!
- form_for #car, :url => { :action => "create" } do |f|
= f.text_field :body
= f.submit t('advert.form.save')
My Controller advert_controller
class AdvertController < ApplicationController
def new
#car = Car.new
end
def create
#car = Car.new(params[:car])
end
end
And my model car.rb
class Car < ActiveRecord::Base
attr_accessible :title, :body
end
But I have a mistake:
undefined method `body' for #
NoMethodError in Advert#new
Showing /my_project/app/views/advert/new.html.haml where line #4 raised
UPD form_for with '=' doesn't work
= form_for #car, :url => { :action => "create" } do |f|
= f.text_field :body
= f.submit t('advert.form.save')
I can see two errors here :
first you should use = instead of - for form_form, indeed = will output the result whereas - won't.
As form_for returns HTML for the form, you want it to be written on the page ;)
As for the cause of your error, well I'm not entirely sure, but it seems that your Car model doeesn't have a body field and so it does not have a body method

Ruby on Rails: How would i stay on the same page if the post is not saved?

def create
#addpost = Post.new params[:data]
if #addpost.save
flash[:notice] = "Post has been saved successfully."
redirect_to posts_path
else
flash[:notice] = "Post can not be saved, please enter information."
end
end
If the post is not saved then it redirects to http://0.0.0.0:3000/posts , but i need to stay on the page, with text input fields so that user can input data.
post model
class Post < ActiveRecord::Base
has_many :comments
validates :title, :presence => true
validates :content, :presence => true
validates :category_id, :presence => true
validates :tags, :presence => true
end
new method
def new
#arr_select = { 1=>"One",2=>"Two" ,3=>"Three" }
#categories_select = Category.all.collect {|c| [ c.category_name, c.id ] }
end
new.html.erb
<h3>Add post</h3>
<%= form_tag :controller=>'posts', :action=>'create' do %>
<%= label :q, :Title %>
<%= text_field :data, :title, :class => :addtextsize %><br/>
<%= label :q, :Content %>
<%= text_area :data, :content, :rows=>10 , :class => :addtextarea %><br/>
<%= label :q, :Category %>
<%= select :data, :category_id, #categories_select %><br/>
<%= label :q, :Tags %>
<%= text_field :data, :tags, :class => :addtextsize %><br/>
<%= label :q, :Submit %>
<%= submit_tag "Add Post" %>
<% end %>
What should i do ?
flash.now with render is what you're looking for.
flash.now[:notice] = "Post can not be saved, please enter information."
render :new
Also instead of
flash[:notice] = "Post has been saved successfully."
redirect_to posts_path
you can just write
redirect_to posts_path, :notice => "Post has been saved successfully."
and it will do the same thing. It works only with redirect_to though, not with render!
Something like this should do what you want:
flash[:notice] = "Post can not be saved, please enter information."
render :new
UPDATE: You updated your question so I have to update my answer. Render is the right way to do this. However, it looks like you load some categories and some other collection of stuff in your new method. Those same instance variables should be available to your create method. The cleanest way to do this is put them into another method and have that method used as a before_filter applied to both create and new. Something like this:
before_filter :load_stuff, :only => [:create, :new]
def load_stuff
#arr_select = { 1=>"One",2=>"Two" ,3=>"Three" }
#categories_select = Category.all.collect {|c| [ c.category_name, c.id ] }
end
Then your new method is pretty much blank and calling render :new in your create method should work.
Hey this answer is super late but thought I'd add it for anyone that comes across it. Probably the most simple solution for what you want to achieve is to add required: true to all of the form inputs you want filled out. E.g
f.text_field :title, required: true, class: "whateverclassyouwant"
This way the form will ONLY be submitted if these fields have been filled in correctly and if not an error flash message will pop up on the field that it needs to be completed. The default flash messages that pop up can be custom styled also, Google how to do so.
This way you can remove the else redirect all together in your create method as it will never get to that point, and just have the if save, flash success etc.

can't change text area style to field_with_error

I'm dealing with this legacy form for creating a new conversation. It has two fields : Name and description (the first comment of a conversation)
Here are the fields :
_fields.haml
.conversation_title= f.label :name, t('.name')
.clear
= f.text_field :name, :style => 'width: 230px'
= errors_for f.object, :name
if f.object.new_record?
= f.fields_for :comments, f.object.comments.build do |comment_fields|
.conversation_title= comment_fields.label :description, t('.description')
= comment_fields.text_area :body, :placeholder => t("comments.new.conversation"), :style => 'width: 545px'
= errors_for f.object, :comments
from the new view for conversations
= form_for [#current_project, #conversation], :html => { 'data-project-id' => #current_project.id, :name => 'form_new_conversation', :multipart => true } do |f| #, :onsubmit => 'return validate_form_new_conversation(form_new_conversation)'
= render 'fields', :f => f, :project => #current_project
= render 'watcher_fields', :f => f, :project => #current_project
The associated validations are :
conversation.rb
validates_presence_of :name, :message => :no_title, :unless => :simple?
validates_presence_of :comments, :message => :must_have_one, :unless => :is_importing
comment.rb
validates_presence_of :body, :unless => lambda { |c| c.task_comment? or c.uploads.to_a.any? or c.google_docs.any? }
For some reason, the proc associated to fields with error from base.rb
##field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
doesn't get called for the text area, so it doesn't change its style to make it turn red. It does for the :name field. Error messages get displayed properly
What am I missing?
Thanks!
The validation would be for the Comment model (rather than the Conversation model) on the body field. Check to make sure that validation exists. You can debug this to make sure that comment_fields.object has an error set on the body field, too.
I failed to notice one important part of this line in your code:
= f.fields_for :comments, f.object.comments.build do |comment_fields|
You call f.object.comments.build which means that you will always end up with a new instance of Comment (rather than the instance that was validated in the controller).
To avoid this you can build a comment in the controller. If you are using the normal restful actions you probably have two places where you want to build a comment. First in the new action and second, in the create action.
def new
#conversation = Conversation.new
#conversation.comments.build # Create a blank comment so that the fields will be shown on the form
end
def create
#conversation = Conversation.new(params[:conversation])
respond_to do |format|
if #conversation.save
format.html { redirect_to conversations_path }
else
format.html {
#conversation.comments.build if #conversation.comments.blank? # Create a blank comment only if none exists
render :action => "new"
}
end
end
end

Resources