Model not validating - ruby-on-rails

I've tried setting up a form validation that would ensure that at least 1 and at most 3 tags must be included in the form. But it isn't working as an empty form is still processed, but a form with 4 comma-seperated tags is validated correctly.
Controller
def update
#product = Product.find(params[:id])
current_user.tag(#product, :with => params[:product][:tag_list], :on => :tags)
if #product.update_attributes(params[:product])
redirect_to :root, :notice => "Added"
else
render :action => 'edit'
end
end
Form
<%= form_for #product do |f| %>
<%= f.label :tag_list, "Your tags" %> <%= f.text_field :tag_list, :value => #product.tags_from(current_user) %>
<p><%= f.submit "Change" %></p>
<%= f.error_messages %>
<% end %>
Model
validate :required_info
validates_size_of :tag_list,
:maximum => 3
private
def required_info
if( tag_list.empty? and description.empty? )
errors.add_to_base "Add one"
end
end

You could use a custom validation:
validates :tag_list_length
private
def tag_list_length
errors.add(:tag_list, "Must include at least one and no more than three tags") unless tag_list.length.between?(1,3)
end

if( tag_list.empty? and description.empty? )
errors.add_to_base "Add one"
end
Just looking at this part of the model, I think you'd rather do if(tag_list.empty? or description.empty?) because you want both of them to be filled.
For the second validation, I'm not an act_as_taggable user so I can't answer you now.

Related

Rails, user input persist when showing activerecord error message

Currently, I have text fields and some validation on the model. However, when I show the error message, all the data that was inserted by the user will be gone. I want to show the error message + the data to persist. Here is the code:
<% if #student.errors.any? %>
<div id="validation_error" class="alert alert-danger" role="alert">
<ul>
<% #student.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for(:user_student, :url => {:controller => 'profile', :action => 'information'}) do |f| %>
<%= f.text_field :first_name, :value => #student.first_name, class: "form-control" , :placeholder => "First Name" %>
<%= f.text_field :last_name, :value => #student.last_name, class: "form-control" , :placeholder => "Last Name" %>
<% end %>
class Student < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
end
UPDATED
Controller:
def information
#student = Student.create(check_new_student_params)
if #student.save
#redirect to other page
end
end
def check_new_student_params
params.require(:user_student).permit(:first_name, :last_name)
end
My expected behaviour: when the user give first name but not last name, it will show the error message and the first name persist on the text field. Thanks
if you have a route to a snew
you should do this
def information
#student = Student.new(check_new_student_params)
if #student.save
#redirect to other page
else
render 'new'
end
end
you're not telling rails to bring back the form after errors. and of course, you should have a new action in your controller.
Here is where your problem is. Your trying to create the object rather than assigning and trying to save. Upon save failing, you should render the page again
def information
#student = Student.new(check_new_student_params)
if #student.save
#redirect to other page
else
render original page again
end
end
def check_new_student_params
params.require(:user_student).permit(:first_name, :last_name)
end

Destroy nested fields in rails 4

Good day,
I'm trying to create simple form with list of links in RoR 4 which can be edited and removed.
I've allowed "destroy" at the main post model file
controller->posts.rb
class Post < ActiveRecord::Base
has_many(:links, :dependent => :destroy)
accepts_nested_attributes_for :links, :reject_if => lambda { |a| a[:link].blank? }, :allow_destroy => true
and I'm accepting the parameters for destroy at the create and update controller
def create
#new_post = Post.new(params[:post].permit(:title, :body, :tag_list, links_attributes:[:link, :_destroy]))
if #new_post.save
redirect_to posts_path, :notice =>"Saved!"
else
render new
end
end
def update
#post_to_update = Post.find(params[:id])
if #post_to_update.update(params[:post].permit(:title, :body, :tag_list, links_attributes:[:link, :_destroy]))
redirect_to posts_path, :notice =>"Updated!"
else
render edit
end
end
I'm using jQuery to remove the link field and set its destroy value as "true"
<h1> Edit post </h1>
<%= form_for #post_to_edit do |f|%>
Title <%= f.text_field :title %> </br>
Body <%= f.text_area :body %> </br>
<%= f.fields_for :links do |b| %>
<li class = "enter_link">
<%= b.text_field :link %>
<%= b.hidden_field :_destroy %>
<%= link_to_function "Remove", "remove_fields(this)" %></br>
</li>
<% end %>
Tags <%= f.text_field :tag_list %>
<%= f.submit "Update that bitch!" %>
<% end %>
Javascript
function remove_fields(link) {
$(link).prev("input[type=hidden]").val("true");
$(link).closest(".enter_link").hide();
}
And here's the problem:
suppose I've got a list of 3 links
"link 1"
"link 2"
"link 3"
And I wish to edit that list by removing link number 2 and 3.
once I press update the destroy parameters is passed on to the controller, but it wont delete the original lines.
Now I'll get the following list
"link 1"
"link 2"
"link 3"
**"link 1" (again, after removing link number 2 and 3)**
As always,
your help is appreciated.
Let me make your life easier and recommend this gem called Cocoon (https://github.com/nathanvda/cocoon)
It creates simple nested forms.
Simply paste this code into your Post form view.
f.fields_for :links do |link|
render 'link_fields', :f => link
link_to_add_association 'add link', f, :tasks
With cocoon a partial is needed for the nested form, so create a file called _link_fields.html.erb
and inside make sure you place everything inside a div. Their documentation isnt clear on this, but from experience I do know its required.
<div class="nested-fields">
f.label :link
f.text_field :link
link_to_remove_association "remove link", f
</div>
And thats it!
change this:
def update
#post_to_update = Post.find(params[:id])
if #post_to_update.update(params[:post].permit(:title, :body, :tag_list, links_attributes:[:link, :_destroy]))
redirect_to posts_path, :notice =>"Updated!"
else
render edit
end
end
to this:
def update
#post_to_update = Post.find(params[:id])
if #post_to_update.update(
params[:post].permit(:title, :body, :tag_list,
## add `:id` to this one
links_attributes:[:id, :link, :_destroy])
##
)
redirect_to posts_path, :notice =>"Updated!"
else
render edit
end
end
you have to permit the id in your links_attributes params, so that the records don't get duplicated and for _destroy to work

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.

How to customize a form/POST in Rails?

Hey guys, this has been plaguing me all week long. I am new to Rails, so please be gentle :)
My root problem is I'm trying to write a form_for Post that will use autocompletion on an input to tie the post to a Client. The database is looking for a client_id, not a text name.
So I have tried a custom validation that will lookup the text value and replace it with an id.
My Post.rb file has this:
class Post < ActiveRecord::Base
belongs_to :client
attr_accessor :client_name
attr_accessible :client_name
before_validation_on_create :validate_client
def validate_client
self.client_id = 1 # just a test
end
def client_exists(passed_name)
return Client.where(:full_name => passed_name).first.blank?
end
end
But when I do this, none of the form variables get passed. The database gets all blank entries except for the client_id. How can I accomplish this? Why aren't my form variables being passed in? Many thanks in advance.
Edit 1: added create definition from posts_controller.rb
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html {
redirect_to(posts_url) # redirect_to(#post, :notice => 'Post was successfully created.')
}
format.xml { render :xml => #post, :status => :created, :location => #post }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
Edit 2: Thanks to #apneadiving, I changed the attr_accesible to include the other attributes, and that passes the POST entries into the db.
attr_accessible :client_id, :time, :content, :client_name
But when I change the validate_client function to search for the client_id,
def validate_client
passed_name = self.client_name
if client_exists(passed_name)
c = Client.where(:full_name => passed_name).first
self.client_id = c.id
else
errors.add_to_base "Error"
end
end
It always gives me this error:
Called id for nil, which would
mistakenly be 4 -- if you really
wanted the id of nil, use object_id
Edit 3: Here's my post form. I can't get the value of :client_name properly.
<%= form_for(#post) do |f| %>
<%= render 'common/error_messages', :object => f.object %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :size => "50x6" %>
</div>
<div class="field">
<div class="sub-field">
<%= f.label :client_name %><br />
<%= f.text_field :client_name, :size => "26" %>
</div>
<div class="sub-field">
<%= f.label :date %><br />
<%= f.text_field(:time, :class => 'date', :size => "10", :value => Date.today.strftime('%m/%d/%Y')) %>
</div>
<div class="sub-field">
<%= f.label :time %><br />
<%= f.time_select :time %>
</div>
</div>
<div class="actions clear">
<%= f.submit %>
</div>
<% end %>
Edit 4: The solution. I was struggling to get the :client_name due to my text being too far tabbed in (I was originally privatizing the function and then took the word "private" out). A modified version of #apneadiving's answer solved it for me!
class Post < ActiveRecord::Base
belongs_to :client
attr_accessor :client_name
validate :validate_client
def validate_client
passed_name = self.client_name
unless client = Client.find_by_full_name(passed_name).blank?
self.client_id = Client.find_by_full_name(passed_name).id
else
errors.add(:client_name, "'#{passed_name}' cannot be found.")
end
end
end
Beware of your attr_acessible:
if you set one, you have to set all the variables that can be set through params. Otherwise they are protected against mass assignment
EDIT 1:
seems c.id is nil which means your client_exists function doesn't work as expected. Try the following (not tested):
def validate_client
unless client = Client.find_by_full_name(client_name).nil?
client_id = client.id
else
errors.add_to_base "Error"
end
end
Your client_exists code is wrong, it returns true if it doesn't exist.
Also, since your client_exists and the validation reuse the same query i would rewrite that a bit.
def validate_client
passed_name = self.client_name
existing_client = Client.where(:full_name => passed_name).first
if existing_client
self.client_id = existing_client.id
else
errors.add_to_base "Error"
end
end

Rails form submit problem

I've get a little problem.
My controller:
def new
#company = Company.new
#title = "Create company"
end
def create
#company = Company.new(params[:company])
#company.admin_id = current_user.id
if #company.save
flash[:success] = "Company created!"
redirect_to admin_path
else
#title = "New company"
render 'new'
end
end
new.html.erb
<%= debug params[:company] %>
<% form_for #company, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.submit "Submit company!" %>
</div>
<% end %>
Company.rb model
validates :name,
:presence => true,
:length => { :maximum => 20 }
validates_attachment_presence :logo
But after submitting form I've get anyway only one error:
Name can't be blank
Of course I'm filling name and logo fields.
Any ideas? Thanks.
You didn't include your _fields partial, and that's probably where your problem is.
Make sure your inputs have appropriate name attributes.
Try to instantiate your model in console and see if validations really work.
You can try something like this: c = Company.new; c.valid?; c.errors and you'll see your errors hash in console.

Resources