Nested attributes form error message customization - ruby-on-rails

I have the following model
class OrgPerson < ActiveRecord::Base
has_and_belongs_to_many :TypRole
has_and_belongs_to_many :OrgContact
has_one :OrgCredential, dependent: :destroy
belongs_to :OrgCompany, foreign_key:"org_company_id"
belongs_to :TypPosition, foreign_key:"typ_position_id"
validates :first_name, presence: true
validates :last_name, presence: true
accepts_nested_attributes_for :OrgCredential
end
class OrgCredential < ActiveRecord::Base
belongs_to :OrgPerson, foreign_key:"org_person_id"
validates :user_name, presence: true
validates :password, length: { minimum: 6 }
before_create :create_remember_token
has_secure_password
end
and the following strong parameters:
def person_params
params.require(:org_person).permit(:first_name, :last_name,
org_credential_attributes: [:password, :password_confirmation])
end
and the following new.html.erb
<%= form_for(#person) do |f| %>
<%= render 'shared/error_messages' %>
<div class="col-md-12 ">
<%= f.text_field :first_name, placeholder: "First Name", :class => "form-control" %>
<%= f.text_field :last_name, placeholder: "Last Name", :class => "form-control" %>
<%= f.fields_for :org_credentials do |oc|%>
<%= oc.password_field :password, placeholder: "Password", :class => "form-control" %>
<%= oc.password_field :password_confirmation, placeholder: "Password Confirmation", :class => "form-control" %>
<% end %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
</div>
In shared/error_messages
<% if #person.errors.any? %>
<div id="error_explanation" class="col-md-12">
<div class="alert alert-danger" role="alert">
The form contains <%= pluralize(#person.errors.count, "error") %>.
</div>
<ul>
<% #person.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
The problem as shown in the picture is that it says something like "Org credential password is too short" when I only want it to say "Password is too short." I'm quite new to rails and I feel like I have to iterate through the error hash to spit out "Password is too short," but i'm not quite certain if this is the only way to do it or if there is an easier way. I hope someone who may have ran into the same problem can enlighten me on how they solved it.
Thank you!

Associations
Firstly, you need to be aware that Rails requires snake_case for the association name definitions - and CamelCase for the Class Names:
#app/models/org_person.rb
Class OrgPerson < ActiveRecord::Base
has_and_belongs_to_many :typ_roles
has_and_belongs_to_many :org_contacts
has_one :org_credential, dependent: :destroy
belongs_to :org_company
belongs_to :typ_position
validates :first_name, presence: true
validates :last_name, presence: true
accepts_nested_attributes_for :org_credential
end
--
Messages
In regards to your error messages, and as Rahul Singh recommended, you need to consider that you can define custom error messages for your validations
You can do that using the the following:
validates :first_name, :last_name,
presence: { message: "You Need To Enter A Name!" }
This will give you custom messages, however, there is a sticking point with Rails, in that it's difficult to get the attribute name to be customized.
If you wanted to show the messages only, you'll be best using something like this (live code):
<% #person.errors.each do |attr,msg| %>
<li><%= msg %></li>
<% end %>

you can add custom validation error message in your model.
class OrgCredential < ActiveRecord::Base
belongs_to :OrgPerson, foreign_key:"org_person_id"
validates :user_name, presence: true
validates :password, length: { minimum: 6 }, message: "Password is too short"
before_create :create_remember_token
has_secure_password
end
read through here Active Record Validations for more details.

Thanks everyone for you answers.
I didn't know that Rails had a sticking point to it, thanks Rich.
I actually used Michal's response to point me to the right direction.
Seems like the only way to actually get rid of the attribute name is to
actually customize every single error message in the en.yml file within configs/locales.
The following guide is a very useful link that helped me fix this problem:
http://adamhooper.com/eng/articles/5
If anyone is running into the same problems, please refer to adam hopper's article.
The rails guide: http://guides.rubyonrails.org/i18n.html#pluralization was of some help, but not as much as the first resource.
Cheers!

Related

Rails association cannot be blank

I'm building off the tutorial app in ruby on rails for a project, and I'm trying to create an association between two models.
In my database, there are users, events, and an attendance table that associates with the email from a user and a code from an event.
I've tried to research how to do this myself, but every time I try to validate the attendance email to a user, it states that the user cannot be blank as if I were trying to create a new one.
Still quite new to Ruby on Rails, so any advice would be appreciated! The models are below.
User Model:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :attendances, inverse_of: :user
accepts_nested_attributes_for :attendances
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates(:name, presence: true, length: { maximum: 50 })
validates(:email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false})
validates(:password, length: { minimum: 6 } )
validates(:password_confirmation, presence: true)
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Attendance Model:
class Attendance < ActiveRecord::Base
attr_accessible :code, :email
belongs_to :user
validates_presence_of :user
end
So far I'm only trying to enforce the association between User and Attendance, once I get that working I'll do the same to Events. Also, this is Rails 3.2.19 and Ruby 1.9.3.
EDIT: Here is the code I'm using for the form, I believe that it works because until I put the validation into the model it was creating rows into the Attendance table.
<% provide(:title, 'Test Event') %>
<h1>Attendance Registration</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#attendance) do |f| %>
<%= render 'shared/attendance_error_messages' %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :code %>
<%= f.text_field :code %>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Also, here's the attendance controller, if that helps.
class AttendancesController < ApplicationController
def new
#attendance = Attendance.new
end
def create
#attendance = Attendance.new(params[:attendance])
if #attendance.save
flash[:success] = "Attendance logged."
redirect_to root_path
else
render 'new'
end
end
end
Add this line to your form to avoid the user presence error
<%= f.hidden_field :user_id, current_user.id %>

Nested Model validation errors show on create but not update

I have a Developer model that :has_one User model. This allows for authentication and stuff across different user types.
When I create a new Developer with incorrect User data, it renders the list of validation errors. When I update a Developer with incorrect User data, it just re-renders the edit form (as it should) but doesn't show the validation errors.
My validation error display code sits in my fields partial for the form so that shouldn't make a difference.
I feel like the issue is in the way I'm trying to update my models.
def update
#developer = Developer.find(params[:id])
if #developer.user.update_attributes(params[:user]) && #developer.update_attributes(params[:developer])
flash[:success] = "Profile Updated"
sign_in #developer.user
redirect_to #developer
else
render 'edit'
end
end
and my User validations aren't anything fancy:
validates :name, presence: true, length: {maximum: 30}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: {minimum: 6}
validates :password_confirmation, presence: true
I've read at least 10 different similar-sounding posts but I haven't found anything that's helped. Any help would be great.
Edit
When I submit my update form, the following params come through
Parameters: {"utf8"=>"✓", "authenticity_token"=>"70xmNVxxES7lK2bSIIul/i5GaiJhB9+B5bV/bUVFlTs=", "user"=>{"name"=>"foo", "email"=>"foo#example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "developer"=>{"skype_name"=>""}, "commit"=>"Save changes", "id"=>"11"}
It still doesn't do the User validations. If I do the following via the console, it works though (i.e. it saves when the params are good and fails when the params are bad):
Developer.last.update_attributes(:user_attributes => {:name => "test updated", :email => "test#example.com", :password => "123456", :password_confirmation => "123456"})
So the only thing that seems different to me is the :user_attributes rather than just :user that my form is giving me. How do I change that?
Edit 2
Relevant part of my _fields partial for the form:
<%= render 'shared/error_messages' %>
<%= fields_for :user do |user| %>
<%= user.label :name %>
<%= user.text_field :name %>
<%= user.label :email %>
<%= user.text_field :email %>
<%= user.label :password %>
<%= user.password_field :password %>
<%= user.label :password_confirmation, "Confirm Password" %>
<%= user.password_field :password_confirmation %>
<% end %>
and my Developer#edit action:
def edit
#developer = Developer.find(params[:id])
end
No need to save user and developer separately, you can manage to save the user through developer model like this,
<%= form_for(#developer) do |f| %>
... developer's attribute ...
<%= f.fields_for :user do |ff| %>
... user's attribute ...
in controller, only
#developer = Developer.find(params[:id])
if #developer.update_attributes(params[:developer])
....
In developer model, you just need to add,
accepts_nested_attributes_for :user
and
attr_accessible :user_attribute
now form_for will automatically display the validation errors of user's model as well.
see this link for more details http://rubysource.com/complex-rails-forms-with-nested-attributes/

foreign key not updating

I'm developing an app for college where a user can log on & upload details of a hiking trail.
So far everything is working & I have also implemented a nested form for photos in each hiking trail. A user can log-on & create a hike.
I would like to display all the hikes which the user created in there show/profile page but when I've set up the relationships in my database & the has_many & belongs_to options in my model. I've also tried to do this with nested accepts_nested_attributes_for :hikingtrails it does none of this works.
I've checked my database when a hikingtrail is created by a user it is not updating the user_id field in the table.
I'm not sure if I'm approaching this entirely the wrong way, should I be looking at polymorphic associations?
class User < ActiveRecord::Base
attr_accessible :user_name, :email, :password, :password_confirmation, :photos_attributes, :hikingtrails_attributes
has_many :hikingtrails
accepts_nested_attributes_for :hikingtrails, :allow_destroy => :true, :reject_if => :all_blank
class Hikingtrail < ActiveRecord::Base
attr_accessible :description, :name, :looped, :photos_attributes,:directions_attributes, :user_id
has_many :photos
has_many :trails
has_many :directions
belongs_to :user
users/show.html.erb
<div class="page-header">
<h1>Your Profile</h1>
</div>
<p>
<b>username:</b>
<%= #user.user_name %>
</p>
<p>
<b>email:</b>
<%= #user.email %>
</p>
<h4>Small Photos</h4>
<% #user.photos.each do |photo| %>
<%= image_tag photo.image_url(:thumb).to_s %>
<% end %>
<h4>Hiking Trails</h4>
<% #user.hikingtrails.each do |hk| %>
<%= hk.name %>
<% end %>
<%= link_to "Edit your Profile", edit_user_path(current_user), :class => 'btn btn-mini' %>
You didn't add :user_id to your accessible attributes in the Hikingtrail model. Try the following:
attr_accessible :description,
:duration_hours,
:duration_mins,
:name,
:looped,
:addr_1,
:addr_2,
:addr_3,
:country,
:latitude,
:longitude,
:photos_attributes,
:trails_attributes,
:directions_attributes,
:user_id
UPDATE:
After seeing the form code, I think it's probably not necessary to do the above and could potentially also be unsafe. Instead, don't set the user_id through mass assignment, but handle user assignment in your controller like so:
class HikingtrailsController < ApplicationController
# ...
def create
#hikingtrail = Hikingtrail.new(params[:hikingtrail])
#hikingtrail.user = current_user
if #hikingtrail.save
# ...
else
# ...
end
end
end
Hope this helps :)

"undefined method `build' for Model(id: integer, created_at: datetime, updated_at: datetime):Class "

I want to create the functionality
1) User enters first name, last name into a form
2) The form is submitted
3) The show view is loaded, and certain parts of the text are replaced by the parameters
4) If the form is invalid, i.e first name presence is not true, then an error is displayed
I've managed to create the functionality for steps 1-3. When a person enters details into a form, it transfers over to a different view. Now, what I would like to do is to validate certain parts of the form.
None of this information is being saved in a database, but I remember that I could use Models to validate forms.
What I tried to do is to create a Model, add an attr_accessor, and validations to it. When the form POSTs, it would build a varaible inside the create action, and I could do an if/than statement.
When I tried this though, I got the error:
NoMethodError in ContentController#create
undefined method `build' for Content(id: integer, created_at: datetime, updated_at: datetime):Class
Here's what my Content#create looks like
def create
#content = Content.build(:params)
if #content.valid?
render 'content/show'
else
render 'content/new'
end
end
This is the view with the form :
<%= form_tag("/content", :method => "post", :id => "form" ) do %>
<h4>The Doctor's Name</h2>
<div class="row">
<div class="span4">
<%= label_tag(:first_name, "First Name") %>
<%= text_field_tag :first_name, nil, placeholder: "ex: James" %>
</div>
<div class="span4">
<%= label_tag(:last_name, "Last Name") %>
<%= text_field_tag :last_name, nil, placeholder: "ex: Bond" %>
</div>
<div class="span4">
<%= label_tag(:full_name, "Full Name") %>
<%= text_field_tag :full_name, nil, placeholder: "ex: James Bond" %>
</div>
</div><!--form_item row-->
<div class="form_explanation">
<div class="alert alert-block">
Enter's The Doctor's First Name, Last Name, And Than The Full Name. Do NOT include "Dr."
</div>
</div>
<div class="pagination-centered">
<%= submit_tag( raw("Generate The Content!"), :class => "btn btn-large btn-primary", :style => "font-weight:bold") %>
</div>
<% end %>
This is what my model looks like :
class Content < ActiveRecord::Base
attr_accessible :first_name, :last_name, :full_name
attr_accessor :first_name
attr_accessor :last_name
attr_accessor :full_name
validates :first_name, presence: true
validates :last_name, presence: true
validates :full_name, presence: true
end
Am I on the right track at all?
The phrase I was looking for that solved my problem was "tabless models".
This tutorial shows you how to do it -- http://railscasts.com/episodes/219-active-model
Instead of using form_tag, you can now use form_for, this is how my model ended up looking like :
class Content
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :first_name, :last_name, :full_name
validates :first_name, presence: true
validates :last_name, presence: true
validates :full_name, presence: true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end

Nested model forms doesnt work on rails

I'm really new to both ruby on rails and programming. I am trying to develop an application but i am stucked now. I was watching http://railscasts.com/episodes/196-nested-model-form-part-1 to make nested model forms but i am having an error. My problem details are as follows;
I have employers model, and employers model has_many interviews, and interview model has_many customquestions. I'm trying to create a form through which i will collect info to create interview. Although i made all necessary assosications, when i submit the form it raises error saying that "Customquestions interview can't be blank". I am kinda sure that it is because of that i miss some code in interview controller. Below you can see my interview controller and the form template that i am using to submit info.
Interview Controller
class InterviewsController < ApplicationController
before_filter :signed_in_employer
def create
#interview = current_employer.interviews.build(params[:interview])
if #interview.save
flash[:success] = "Interview created!"
redirect_to #interview
else
render 'new'
end
end
def destroy
end
def show
#interview = Interview.find(params[:id])
end
def new
#interview = Interview.new
3.times do
customquestion = #interview.customquestions.build
end
end
end
Form which i use to submit info:
<%= provide(:title, 'Create a new interview') %>
<h1>Create New Interview</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#interview) do |f| %>
<%= render 'shared/error_messages_interviews' %>
<%= f.label :title, "Tıtle for Interview" %>
<%= f.text_field :title %>
<%= f.label :welcome_message, "Welcome Message for Candidates" %>
<%= f.text_area :welcome_message, rows: 3 %>
<%= f.fields_for :customquestions do |builder| %>
<%= builder.label :content, "Question" %><br />
<%= builder.text_area :content, :rows => 3 %>
<% end %>
<%= f.submit "Create Interview", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
In interview model, i used accepts_nested_attributes_for :customquestions
Interview Model
class Interview < ActiveRecord::Base
attr_accessible :title, :welcome_message, :customquestions_attributes
belongs_to :employer
has_many :customquestions
accepts_nested_attributes_for :customquestions
validates :title, presence: true, length: { maximum: 150 }
validates :welcome_message, presence: true, length: { maximum: 600 }
validates :employer_id, presence: true
default_scope order: 'interviews.created_at DESC'
end
The validation error gets raised in the customquestions model because (I assume) it validates :interview_id. The problem is that interview_id won't get set until the parent object (Interview) is saved, but validations for customquestion are run before Interview is saved.
You can let cusomtquestions know about this dependency by adding the option :inverse_of=> :customquestions to belongs_to :interview in the customquestions model.

Resources