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/
Related
I have a user table, after the user is created I want to edit one attribute using the below code. This is my user edit view:
<h1>Please select below</h1>
<%= form_for #user do |f| %>
<div class="form-group">
<%= f.label :extra_activity %>
<%= f.select(:extra_activity, [['P_Act', 1],['Ph_Act', 2], ['C_Act', 3]], class: 'form-control', required: true) %></br>
</div>
<%= f.submit 'Submit', class: 'btn btn-primary btn-lg' %>
<% end %>
In my user controller I have the following methods for edit and update :
def edit
#user = User.find(params[:id])
#users = User.all
#user_id = params[:user_id]
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to new_user_activity_path(#user)
else
redirect_to home_path
end
end
user_params are listed below:
def user_params
params.require(:user).permit(:first_name, :surname, :previous_award, :chosen_award, :email, :password, :password_confirmation, :extra_activity)
end
When i initially create the user, :extra_activity is set to 0. When the user clicks submit on the edit form, nothing happens, the user is redirected to home_path. I just need to update the user's :extra_activity attribute to whatever they select in the form. Can anybody suggest where I am going wrong here? Not sure how to save the selected number, as the updated value for :extra_activity
UPDATE:
class User < ActiveRecord::Base
has_many :activities, dependent: :destroy
has_many :weeks, dependent: :destroy
authenticates_with_sorcery!
validates :password, length: { minimum: 3 }
validates :password, confirmation: true
validates :email, uniqueness: true, email_format: { message: 'has invalid format' }
end
After reading your comments and checking the update, i guess the solution is to add:
validates :password, length: { minimum: 3 }, if: :password
The password is most likely stored in hashed form and not in password column. So if you reload the user the attribute is not set.
The validation should be: IF the password is set, then make sure that it is at least of length 3 (which is pretty short).
You should make sure that the password that is stored is not changed when you update the user through this controller.
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 %>
I have 2 types of Users: Normal users and pros.
Pros are Users, but have extra fields in a separate table called :pros.
So , I did a separate registration form for :pros, in which I included :users fields, and added a fields_for with the new :pro fields.
I also added those new parameters to application_controller, so that devise permits them.
When submiting the registration form, the user is created, but I get the following error in my logs:
Started POST "/users" for 127.0.0.1 at 2014-11-13 00:53:43 +0100
Processing by RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zUVLJFHhShoHvUVneGNmCf46E4KPWaINeTw4o7iCa7w=", "user"=>{"name"=>"asdasd", "email"=>"asdasd#sss.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "pros"=>{"type"=>"Marca de Decoración", "web"=>"asadasd", "telephone"=>"765876", "about"=>"sadasd"}, "tos_agreement"=>"1"}, "commit"=>"Registrarme y aplicar para PRO"}
Unpermitted parameters: pros
My view is:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= f.label :name, "Nombre de usuario" %>
<%= f.text_field :name, :autofocus => true %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.fields_for :pro do |pro| %>
<%= pro.select :type,["Marca de Decoración","Tienda de Decoración","Blogger"] %>
<%= pro.text_field :web, placeholder: "http://www.miweb.com" %>
<%= f.label :telephone, "Teléfono" %>
<%= pro.text_field :telephone, placeholder: "Teléfono", label: "Teléfono de contacto" %>
<%= pro.text_field :about%>
<% end %>
Users Controller new action
def pro_new
render "devise/registrations/new-pro-registration"
#user = User.create
end
My model relations:
User.rb
has_one :pro
accepts_nested_attributes_for :pro, allow_destroy: true
Pro.rb
belongs_to :user
My application controller:
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :tos_agreement, :avatar, :avatar_cache, :email, :password, :password_confirmation, pros_attributes: [:pro_name, :pro_image, :is_active, :web, :user_id, :about, :facebook, :twitter, :linkedin, :telephone]) }
end
I completely agree with #smallbutton.com
You need to change pro_attributes instead of pros_attributes. you can use params.require(:user).permit! if you want to accept all the params.
You have association like user has_one :pro.
Then you have passed array in permitted parameters but it is has_one relation.
It should be like this.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :tos_agreement, :avatar, :avatar_cache, :email, :password, :password_confirmation, pro_attributes: (:pro_name, :pro_image, :is_active, :web, :user_id, :about, :facebook, :twitter, :linkedin, :telephone)) }
end
It will works :)
Try to change your controller to this so that a new instance of pro is build and your form helper should produce pro_attributes. I assume this is the new action so you dont call #create but #new on your user. Maybe you need to change pros_attributes to pro_attributes in the permitted_parameters method because its a has_one relation.
def pro_new
#user = User.new
#user.build_pro
render "devise/registrations/new-pro-registration"
end
Hope this will do the trick.
I could give you a quick answer, but I think your design needs some work. You'd be far better of (especially in the long run) with the following format using single table inheritance and delegations
User #(base class)
has_one :userInfo
delegate :list_of_pro_fields, to: :userInfo
BasicUser < User #(sub class)
Pro < User #(sub class)
validates presence: true, [:list_of_pro_fields]
UserInfo #(base class)
belongs_to :user
While you could put the relationship between Pro and ProInfo I'd keep it on the base class if a user is able to lose (and regain) their pro status. This will prevent orphan records in UserInfo of ex-Pros.
This is also going to result in far better data segregation and response times as many fields you wont need a lot of the time are being moved to a separate table which can be joined as required.
I'm relatively new to Rails (using Rails 4), and am having a problem with validation for my user model. Even when the form is fully filled in with both the passwords, when I submit the code two errors print out:
{:password=>["can't be blank"], :password_confirmation=>["doesn't match Password"]}
I would like the user to be saved into the database, but these validation errors are preventing that from happening. What I would like to know is what I need to change in order to get rid of these errors.
I am printing out the params object and it looks like this (the authenticity token is omitted here):
params: {"utf8"=>"✓","authenticity_token"=>"[omitted]",
"user"=>{"username"=>"testuser1", "password"=>"test",
"password_confirmation"=>"test", "email_attributes"=>{"email"=>"d#d.com"},
"first_name"=>"test", "last_name"=>"user", "gender"=>"male", "city"=>"la",
"state"=>"ca", "country"=>"usa", "dob"=>"1980-11-20"},
"commit"=>"Create Account", "action"=>"create", "controller"=>"users"}
So it appears that the password and password_confirmation attributes are getting passed correctly. I am wondering if this may have to do with the virtual attribute password I have defined in the user model, but if that is the case I am still not quite sure how to solve this problem. Any help would be greatly appreciated. Let me know if I need to elaborate further.
Here is relevant code for reference:
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_email
end
def create
if #user = User.create(user_params)
logger.debug "#{#user.errors.messages}"
logger.debug "params: #{params}"
redirect_to :action => "new"
else
logger.debug "#{#user.errors.messages}"
logger.flush
redirect_to :action => "new"
end
end
private
def user_params
params.require(:user).permit(:username, :password, :password_confirmation, :first_name, :last_name, :gender, :dob, :city, :state, :country, :admin_level, email_attributes: [:email])
end
end
Model:
class User < ActiveRecord::Base
has_one :email
validates_presence_of :username, :email, :password
validates_confirmation_of :password, :on => :create
accepts_nested_attributes_for :email
def password_valid?(candidatePass)
candidatePassAndSalt = "#{candidatePass}#{self.salt}"
candidatePasswordDigest = Digest::SHA1.hexdigest(candidatePassAndSalt)
if (candidatePasswordDigest == self.password_digest)
return true
else
return false
end
end
def password
end
def password=(text)
self.salt = Random.new.rand
passAndSalt = "#{text}#{self.salt}"
self.password_digest = Digest::SHA1.hexdigest(passAndSalt)
end
end
View:
<%= form_for #user, url: {action: "create"}, html: {class: "user-creation-form"} do |f| %>
<%= f.text_field :username %>username<br/>
<%= f.password_field :password %>pw<br/>
<%= f.password_field :password_confirmation %>pwcopy<br/>
<%= f.fields_for :email do |email_form| %>
<%= email_form.text_field :email %>email<br />
<% end %>
<%= f.text_field :first_name %>first<br/>
<%= f.text_field :last_name %>last<br/>
<%= f.radio_button :gender, "male" %>
<%= f.label :gender_male, "M" %>
<%= f.radio_button :gender, "female" %>
<%= f.label :gender_female, "F" %><br />
<%= f.text_field :city %>city<br/>
<%= f.text_field :state %>state<br/>
<%= f.text_field :country %>country<br/>
<%= f.date_field :dob %>dob<br/>
<%= f.submit "Create Account" %><br/>
<% end %>
The issue is your empty getter:
def password
end
It always return nil.
2 small additions to the previous answer, which should resolve your issue by the way.
1) If you're using Rails >3 (I assume you are by looking at your user_params method in the controller) you don't have to specify all those password fields and validations.
ActiveRecord automatically includes this ActiveModel method :
has_secure_password
More details at : http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
2) If the uncrypted password/password_confirmation are shown in your log files your app is insecure. Add this to your config/application.rb :
config.filter_parameters = [:password, :password_confirmation]
This should not be needed if you are using has_secure_password in your User model.
I've been working through the Ruby on Rails Tutorial by Michael Hartl. Currently, in order to edit any of the User attributes, the user must confirm their password. Is there any way to update the user attributes without having to do this?
My form looks like this:
<%= form_for #user do |f| %>
<div class="field">
<%= f.label :course1 %><br />
<%= f.text_field :course1 %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>"
and my update definition in users_controller.rb looks like this:
def update
if #user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
Currently, the update_attributes action fails.
Thanks!
To slightly refine Dylan's answer, you need to define that password_changed method which was giving you the error. I used a different name, as I don't care if the password has been changed.
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:unless => :already_has_password?
private
def already_has_password?
!self.encrypted_password.blank?
end
On your User model, you probably have something along the lines of:
validates_presence_of :password_confirmation
Add an if clause as follows, that way it only checks for the confirmation when the password is actually being changed:
validates_presence_of :password_confirmation, :if => :password_changed?
If you are using bcrypt to encrypt the password, here is the code that worked for me on Rails 4
#--Code for User method
validates :password, presence: true, confirmation: true, :unless => :already_has_password?
#
private
def already_has_password?
!self.password_digest.blank?
end