Nested Forms, Validations and Error Messages - ruby-on-rails

So after two days of reading up on this topic I feel lost between problems in older versions of rails and some individual stuff that I probably did wrong.
I have a form where a User can create an account. Every user needs to put in his number plate and a car is created if it does not yet exist with that number plate and assigned to the user.
My form shows the validation error messages for the user, but not for the car (it only marks the input fields for the number plates red).
So my questions is: How do I get the error messages to render be shown properly?
I also suspect after all the topics I read, that my user controller might be doing some stuff that I should not do manually.
My form:
<%= form_for(#user, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="row">
<div class="col-xs-6 col-xs-offset-3">
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Bestätigung" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-xs-offset-3">
<%= f.fields_for :car, #car do |car| %>
<%= car.label :plateprefix, "Nummernschild" %>
<div class="form-inline">
<%= car.text_field :plateprefix %>
<%= car.label :plate, "-" %>
<%= car.text_field :plate %>
</div>
<% end %>
</div>
</div>
user.rb:
class User < ActiveRecord::Base
belongs_to :car, inverse_of: :users
validates :name, presence: true, length: { maximum: 50 }
car.rb:
class Car < ActiveRecord::Base
has_many :users, inverse_of: :car
accepts_nested_attributes_for :users
validates :plateprefix, presence: true
and finally my car controller:
def new
#user = User.new
end
def create
if verify_recaptcha
#user = User.new(user_params)
if Car.exists?(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#car = Car.find_by(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#user.car_id = #car.id
else
#car = Car.new(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#car.country = 'D'
#car.save
#user.car_id = #car.id
end
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
#user = User.new
render 'new'
end
else
#user = User.new
render 'new'
end
end

Ok, so after 2 further days of reading I solved it:
First I moved the accepts_nested_attributes from the car.rb to the user.rb (and from plural to singular)
user.rb
accepts_nested_attributes for :car
but then for some reason in my form I had to change the variable in the fields_for to plural (:cars - some of rails magic is beyond me but I am getting there I think)
Next i also recreated my controller to use the methods provided by ActiveModel/Record and went to do:
if Car.exists?(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
#user.car = Car.find_by(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
else
#user.create_car(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
end
So this cleaned up my code a bit and I finally got my errors to show properly!

Related

Not able to save data into Model

I can't save the data into my model. Every time when the code run it will ran into the else statement which failed to save the data in the CREATE action. Any idea?
This is my invoices_controller.rb
class InvoicesController < ApplicationController
def new
#permits = Permit.find(params[:permit_id])
#invoice = Invoice.new
end
def create
#permit = Permit.find(params[:permit_id])
#invoice = #permit.build_invoice(invoice_params)
if #invoice.save
redirect_to payment_path
else
redirect_to root_path
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_invoice
#invoice = Invoice.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def invoice_params
params.require(:invoice).permit(:vehicle_type, :name, :department, :carplate, :duration, :permitstart, :permitend, :price, :time)
end
end
Invoices/new.html.erb ( This is the data I wanted to save)
<% provide(:title, 'Invoice') %>
<h1>Invoice</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3" id="datashow">
<%= form_for(#invoice) do |f| %>
<h2>Time : <%=#permits.created_at%></h2></br>
<h2>Invoice ID : <%=#permits.id%></h2></br>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, :value => #permits.vehicle_type, readonly: true %>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, :value => #permits.carplate, readonly: true %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, :value => #permits.studentid, readonly: true %>
<%= f.label :name %>
<%= f.text_field :name, :value => #permits.name, readonly: true %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, :value => #permits.department, readonly: true %>
<%= f.label :permit_start %>
<%= f.text_field :permitstart, :value => #permits.permitstart, readonly: true %>
<%= f.label :permit_end %>
<%= f.text_field :permitend, :value => #permits.permitend, readonly: true %>
<%= f.label :"Price" %>
<%= (f.text_field :price, :value => '$AUD 50' , readonly: true) %>
<%= hidden_field_tag(:permit_id, #permits.id) %>
<%= f.submit "Make Payment", class: "btn btn-primary" %>
<% end %>
</div>
</div>
Invoice.rb
class Invoice < ApplicationRecord
belongs_to :user
has_one :receipt
belongs_to :permit
end
Permit.rb
class Permit < ApplicationRecord
belongs_to :user
has_one :invoice
end
If you are unsure why your object is not created, you have multiple options.
First you can use #invoice.save! instead of #invoice.save during debugging. This will raise an exception and give you some clues, what's going wrong.
Or you can use a debugger and inspect #invoice.errors.full_messages.
Further more you can output #invoice.errors.full_messages via Rails.logger.error #invoice.errors.full_messages.to_sentence.
Or you can use the error message as a flash message flash[:error] = #item.errors.full_messages.to_sentence
This should help you find the error.
from: build method on ruby on rails
build won't "create" a record in database, just create a new object in memory so that the view can take this object and display something, especially for a form.
So build isn't working because you aren't creating (create and saving) a record. build doesn't save a record.
Try:
def create
#permit = Permit.find(params[:permit_id])
#invoice = #permit.invoices.create(invoice_params)
if #invoice.save
redirect_to payment_path
else
redirect_to root_path
end
end

Rails: File to Upload does not get passed from Form to the controller

This is the Form. All of the fields get passed (and saved) except the one containing the File.
I have checked that using the
render plain: params[:article].inspect method
giving out this (I have entered the value "n" for all fields):
{"country"=>"n", "region"=>"n", "town"=>"n", "street"=>"n", "company"=>"n", "title"=>"n", "content"=>"n"}
I am leaving out superfluous fields here to make the Form shorter:
<%= form_for(#article, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :country %>
<%= f.text_field :country, :required => true,
placeholder: "enter country" %>
</div>
<%= f.label :content %>
<%= f.text_field :content, :required => true, placeholder: "town..." %>
</div>
</div>
</div>
</div>
<span class="picture">
<%= form_for #article, html: { multipart: true } do |f| %>
<%= f.text_field :title %>
<%= fields_for :pictures do |ff| %>
<%= ff.file_field :picture %>
<% end %>
<% end %>
</div>
I have also tried the slight variation here, but no change
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
The create method at the Controller is like this:
def create
#article = current_user.articles.build(article_params)
if #article.save
flash[:success] = "Article created!"
redirect_to root_url
else
render 'articles/new'
end
end
and yes, the new method in the Articles controller, is like I was indicated by peers here:
def new
#article = current_user.articles.build
#article.pictures.build
end
The Article Model
class Article < ActiveRecord::Base
belongs_to :user
has_many :pictures
accepts_nested_attributes_for :pictures, allow_destroy: true
And the pictures Model
class Picture < ActiveRecord::Base
belongs_to :article
mount_uploader :picture, PictureUploader
end
Change your <%= fields_for :pictures do |ff| %> to <%= f.fields_for :pictures do |ff| %>

Mass-assignement error in fields_for form using Devise user with multiple roles

I looked on plenty of issues related to mass-assignement in nested attributes but none of them helped me out...
I am using Devise and have one User model with different types, as the Client one:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :remember_me, :rolable_type
belongs_to :rolable, :polymorphic => true
accepts_nested_attributes_for :rolable
end
class Client < ActiveRecord::Base
has_one :user, :as => :rolable
attr_accessible :specialty, :address
end
I have the following form for creating a new user with the client type and client attributes and which is working:
[...]
resource.rolable = child_class_name.constantize.new if resource.rolable.nil?
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
[...]
<% # customized code begin %>
<%= fields_for resource.rolable do |rf| %>
<% if rolable_type == 'client' %>
<div><%= rf.label :specialty %><br />
<%= rf.text_field :specialty %></div>
[...]
<% end %>
<% end %>
<%= hidden_field :user, :rolable_type, :value => rolable_type %>
<% # customized code end %>
[...]
<div><%= f.submit "Sign up" %></div>
<% end %>
I have the following for editing the same user and which is not working: I am getting the following error : ActiveModel::MassAssignmentSecurity::Error in UsersController#update : Can't mass-assign protected attributes: client
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
[...]
<% f.fields_for(#user.rolable) do |rf| -%>
<div><%= rf.label :specialty %><br />
<%= rf.text_field :specialty %></div>
<div><%= rf.label :address %><br />
<%= rf.text_field :address %></div>
<% end %>
[...]
Here is my UsersControllerupdate action:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
you need an accepts_nested_attributes_for :rolable in User class
See: Rails 3.1: accepts_nested_attributes_for and has_one association - won't work?
Solution by OP.
I changed <% f.fields_for(#user.rolable) do |rf| %> for <%= f.fields_for :rolable do |rf| %>.
And it worked

AssociationTypeMismatch (Object expected, got HashWithIndifferentAccess) in Rails app

I'm getting a AssociationTypeMismatch Error and I'm not sure where I'm making a mistake. I'm pretty new to Rails so I'm guessing I'm making some silly mistake. I've checked my syntax and compared it against AssociationTypeMismatch Error on Ruby on Rails app
... but I still can't seem to catch the error.
Here's my models
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference
has_secure_password
validates_uniqueness_of :email
validates_presence_of :full_name
has_one :preference, :dependent => :destroy
accepts_nested_attributes_for :preference
end
class Preference < ActiveRecord::Base
attr_accessible :background_fill, :background_position, :layout
belongs_to :user
end
Here's my controller:
def new
#user = User.new
#user.build_preference
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thanks for signing up!"
else
render "new"
end
end
Here's my view:
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>There's an error!</h2>
<ul>
<% #user.errors.full_message.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :full_name %>
<%= f.text_field :full_name, :class => "target", :placeholder => "Your full name", :maxlength => "55", :autofocus => "autofocus" %>
<%= f.label :email %>
<%= f.email_field :email, :class => "target", :placeholder => "example#gmail.com", :maxlength => "55" %>
<%= f.label :password %>
<%= f.password_field :password, :class => "target", :placeholder => "Enter a password", :maxlength => "55" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, :class => "target", :placeholder => "Enter your password again", :maxlength => "55" %>
<%= f.fields_for :preference do |builder| %>
<%= builder.hidden_field :layout, value: params[:layout] %>
<%= builder.hidden_field :background_fill, value: params[:background_fill] %>
<%= builder.hidden_field :background_position, value: params[:background_position] %>
<% end %>
<%= f.submit "Create an Account", :class => "button cta" %>
<% end %>
And my parameters
{"utf8"=>"✓",
"authenticity_token"=>"[redacted]=",
"user"=>{"full_name"=>"test",
"email"=>"test#example.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"preference"=>{"layout"=>"layout-3",
"background_fill"=>"restaurant-2",
"background_position"=>"position-center"}},
"commit"=>"Create an Account"}
Edit:
The error I get is Preference(#70124732528700) expected, got ActiveSupport::HashWithIndifferentAccess(#70124687315200). My understanding is that #user.build_preference and accepts_nested_attributes_for :preference would just work on its own.
Do I need to create a def preferences_attributes= as per?
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Update
Okay so I think I'm getting a bit closer. Poking around in the rails console, I figured I need to create Preference.new inside the UserController before I can pass in the hash. Since I'm not sure sure what build_preference does exactly, I'm not having any luck yet.
I've tried adding #user.preference = Preference.new above build preference and changing f.field_for :preference to f.field_for #user.preference but I'm still getting the same error.
Update 2
For anyone else that's stuck on this problem, the answer is to change f.field_for :preference to f.field_for :preference_attributes. See comment below by zetetic.
it is:
attr_accessible :preference_attributes
and in your form:
<%= f.fields_for :preference_attributes do |builder| %>
...
Give a shot.
In your User model, try to add :preference_attributes to your attr_accessible line.
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference_attributes
.. # rest of your code goes here
end

Nested form validations not working in rails app

My validations are not working for a nested form - messages, which is in other models show page.
Here's the code:
Reserve Online:
<%= form_for([#trip, #trip.messages.build]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_field :name, :class => "span3", :placeholder => "Name:" %>
<%= f.text_field :email, :class => "span3", :placeholder => "Email:" %>
<div class="h">
<%= f.text_field :subject, :class => "h", :value => (#trip.title) %>
</div>
<%= f.text_area :body, :class => "input-xlarge3", :placeholder => "Message:", :id => "textarea", :rows => "3" %>
<%= f.submit :class => " btn btn-primary btn-large ", :value => "Send Message" %>
<% end %>
</div>
Message.rb
class Message < ActiveRecord::Base
belongs_to :trip
attr_accessible :name, :email, :subject, :body
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :body, :maximum => 500
end
an the messages_controller.rb
class MessagesController < ApplicationController
def create
#trip = Trip.find(params[:trip_id])
#message = #trip.messages.create(params[:message])
if #trip.messages.create
MessageMailer.send_message(#message).deliver
redirect_to thank_you_path
else
redirect_to trip_path(#trip)
end
end
end
_error_messages.rb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
The whole code above works fine, but the validation part is simply ignored. And I don't see any errors.
So I Can't figure out what I'm doing wrong. Can You help?
Thank you!
I think there is a problem in your controller:
def create
#trip = Trip.find(params[:trip_id])
#message = #trip.messages.create(params[:message])
# the following line doesnt make sense
# you're recreating an empty message
# it should be something like "if #message.valid?"
if #trip.messages.create
MessageMailer.send_message(#message).deliver
redirect_to thank_you_path
else
redirect_to trip_path(#trip)
end
end
Try to fix that and see if it helps.

Resources