ruby on rails - validates_presence_of - Actualizando los atributos de "Usuario" sin requerir contraseña
rails validates if (9)
En este momento, los usuarios pueden editar algunos de sus atributos sin tener que ingresar su contraseña porque mis validaciones están configuradas de esta manera:
validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :on => :create
validates :password, :confirmation => true, :length => { :within => 6..40 }, :on => :update, :unless => lambda{ |user| user.password.blank? }
Sin embargo, después de que un usuario haga esto, su contraseña se elimina - update_attributes está actualizando su contraseña a "". Aquí está mi definición de actualización:
def update
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render ''edit''
end
end
También he intentado usar una definición diferente que usa update_attribute en su lugar:
def save_ff
@user = User.find(params[:id])
@user.update_attribute(:course1, params[:user][:course1] )
@user.update_attribute(:course2, params[:user][:course2] )
@user.update_attribute(:course3, params[:user][:course3] )
@user.update_attribute(:course4, params[:user][:course4] )
redirect_to @user
end
Pero por alguna razón esto está haciendo lo mismo. ¿Cómo puedo actualizar algunos atributos de usuario sin cambiar la contraseña? ¡Gracias!
Estaba teniendo el mismo problema. No pude arreglarlo con
params[:user].delete(:password) if params[:user][:password].blank?
Solo he podido hacer que funcione haciendo "update_attribute" en cada elemento individualmente, por ejemplo
if ( @user.update_attribute(:name, params[:user][:name]) &&
@user.update_attribute(:email, params[:user][:email]) &&
@user.update_attribute(:avatar, params[:user][:avatar]) &&
@user.update_attribute(:age, params[:user][:age]) &&
@user.update_attribute(:location, params[:user][:location]) &&
@user.update_attribute(:gender, params[:user][:gender]) &&
@user.update_attribute(:blurb, params[:user][:blurb]) )
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user info"
render ''edit''
end
que es claramente un hack total, pero es la única forma en que puedo resolverlo sin alterar las validaciones y eliminando la contraseña.
He estado luchando con esto y dando vueltas en círculos por un tiempo, así que pensé en poner mi solución Rails 4 aquí.
Ninguna de las respuestas que he visto hasta ahora satisfacen mi caso de uso, todas parecen implicar eludir la validación de alguna manera, pero quiero poder validar los otros campos y también la contraseña (si está presente). Además, no uso dispositivos en mi proyecto, por lo que no puedo hacer uso de nada en particular.
Vale la pena señalar que es un problema de 2 partes:
Paso 1: debe eliminar la contraseña y el campo de confirmación de los parámetros seguros si la contraseña está en blanco, como en su controlador:
if myparams[:password].blank?
myparams.delete(:password)
myparams.delete(:password_confirmation)
end
Paso 2: debe modificar la validación para que la contraseña no se valide si no se ingresa. Lo que no queremos es que se establezca en blanco, por lo que lo eliminamos de nuestros parámetros anteriormente.
En mi caso, esto significa tener esto como la validación en mi modelo:
validates :password, :presence => true, :confirmation => true, length: {minimum: 7}, :if => :password
Tenga en cuenta: if =>: password: omita la comprobación si la contraseña no se está configurando.
No me di cuenta de que la solución que te di ayer llevaría a este problema. Lo siento.
Bien, inspirándose en el diseño , simplemente debe actualizar su controlador de esta manera:
def update
params[:user].delete(:password) if params[:user][:password].blank?
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render ''edit''
end
end
Tuve el mismo problema, y las soluciones anteriores no funcionaron para mí. Encontré al verdadero culpable en mi caso: tuve una devolución de llamada encrypt_password en mi modelo de Usuario, que estaba configurando la contraseña en blanco cada vez.
before_save: encrypt_password
Lo arreglé agregando una condición al final para esta llamada:
before_save: encrypt_password,: menos => Proc.new {| u | u.password.blank? }
Esta entrada de blog demuestra el principio de lo que quieres hacer.
Lo que no se muestra, pero puede ser útil, es agregar accesores al modelo:
attr_accessor :new_password, :new_password_confirmation
attr_accessible :email, :new_password, :new_password_confirmation
y para proporcionar toda la validación deseada bajo la condición de que el usuario haya proporcionado una nueva contraseña.
validates :new_password, :presence => true,
:length => { :within => 6..40 },
:confirmation => true,
:if => :password_changed?
Por último, agregaría una verificación para ver si se ha establecido la contraseña cifrada para determinar si "¿cambio de contraseña?" para requerir una contraseña en un nuevo registro.
def password_changed?
!@new_password.blank? or encrypted_password.blank?
end
2017 respuesta:
En Rails 5 como también lo indica el tutorial Michael Hartl, es suficiente que tengas algo parecido a esto en tu modelo:
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
allow_nil: true es la clave aquí que le permite a un usuario editar su información sin requerir también un cambio de contraseña.
En este punto, se podría pensar que esto también permitirá registros de usuarios vacíos; Sin embargo, esto se evita mediante el uso de has_secure_password
que valida automáticamente la presencia de la contraseña, pero solo el método de create
.
Este es un modelo de usuario de demostración con fines ilustrativos:
class User < ApplicationRecord
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = //A[/w+/-.]+@[a-z/d/-.]+/.[a-z]+/z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
.
.
.
end
No tengo ni idea de cómo hacer esto con el dispositivo. Mis dos centavos.
La respuesta correcta ya no funciona para rieles 4 . Creo que mi respuesta es la más limpia y la más versátil que funcionará siempre que desee omitir atributos (no solo la contraseña). Este enfoque será necesario si desea actualizar los atributos separados de cualquier modelo en diferentes lugares.
Por ejemplo, si desea hacer lo que hace y tener las contraseñas actualizables a través de una página de security
, la imagen del perfil actualizable a través de la vista de usuario y la mayor parte de la información de un usuario actualizable a través de una vista de edición de usuario.
1) Extienda la hash class
con un método de clase para eliminar valores en blanco. Usaremos este método para eliminar los valores en blanco que no se están actualizando pero que aún están presentes en el hash params:
1a) Cree un archivo hash.rb
en su directorio lib
, bajo un directorio ext
:
línea de comando
$ mkdir lib/ext
$ touch lib/ext/hash.rb
1b) Dentro de hash.rb
, ''crea'' una clase Hash
y crea un .delete_blanks!
método:
lib / ext / hash.rb
class Hash
def delete_blanks!
delete_if { |k, v| v.nil? }
end
end
1c) Requerir este archivo (y todo su directorio lib) en los rieles que lo referencian en un inicializador:
config / boot.rb
# other things such as gemfiles are required here, left out for brevity
Dir[''lib/**/*.rb''].each { |f| load(f) } # requires all .rb files in the lib directory
2) Dentro de la acción de actualización # de los usuarios, ¡implemente nuestro nuevo nuevo delete_blanks! Método de clase para eliminar los atributos que no estamos actualizando del hash params. Luego, actualice la instancia de usuario a través del método update_attributes
, * no el método de update
!
2a) En primer lugar, ¡usemos delete_blanks! Método para arreglar nuestro hash user_params:
app / controllers / users_controller.rb
new_params = user_params.delete_blanks!
2b) Y ahora, actualicemos la instancia utilizando el método update_attributes
, (nuevamente, no el método de update
):
app / controllers / users_controller.rb
@user.update_attributes(new_params)
Aquí es cómo debe verse la acción de users#update
finalizados:
app / controllers / users_controller.rb
def update
new_params = user_params.delete_blanks!
if @user.update_attributes(new_params)
redirect_to @user, notice: ''User was successfully updated.''
else
render action: ''edit'' // or whatever you want to do
end
end
3) En el modelo de User
, agregue la opción if: :<attribute>
a todas sus validaciones. Esto es para asegurarse de que la validación solo se active si el atributo está presente en el hash params. ¡Nuestros delete_blanks!
El método habrá eliminado el atributo del hash params, por lo que la validación de la contraseña, por ejemplo, no se ejecutará. También vale la pena señalar que delete_blanks!
solo elimina las entradas de hash con un valor de nil , no aquellas con cadenas vacías. Entonces, si alguien omite la contraseña en el formulario de creación del usuario (o cualquier formulario con un campo para la contraseña), la validación de la presencia tendrá efecto porque: la entrada de contraseña del hash no será nula, estará vacía cuerda:
3a) Use la opción if:
en todas las validaciones:
aplicación / modelos / usuario.rb
VALID_EMAIL_REGEX = /[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+/.[a-zA-Z0-9/-.]/
validates :first_name, presence: true, if: :first_name
validates :last_name, presence: true, if: :last_name
validates :user_name, presence: true, if: :user_name
validates :email, presence: true,
uniqueness: { case_sensitive: false },
format: { with: VALID_EMAIL_REGEX }, if: :email
validates :password, length: { minimum: 6, maximum: 10 }, if: :password
Y eso es. Ahora, el modelo de usuario se puede actualizar en muchas, muchas formas diferentes en toda su aplicación. Las validaciones de presencia para un atributo aún entran en juego en cualquier formulario que contenga un campo para él, por ejemplo, la validación de presencia de contraseña todavía entraría en juego en la vista de user#create
.
Esto puede parecer más detallado que otras respuestas, pero creo que esta es la forma más sólida. Puede actualizar de forma aislada un número infinito de atributos para instancias de User
, en una cantidad infinita de modelos diferentes. Solo recuerde que cuando quiera hacer esto con un nuevo modelo, debe repetir los pasos 2a) , 2b) y 3a)
# It smells
def update
if params[:user][:password].blank?
params[:user].delete :password
params[:user].delete :password_confirmation
end
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render ''edit''
end
end
# Refactoring
class User < ActiveRecord::Base
...
def update_attributes(params)
if params[:password].blank?
params.delete :password
params.delete :password_confirmation
super params
end
end
...
end
def update
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render ''edit''
end
end
# And little better
class User < ActiveRecord::Base
...
def custom_update_attributes(params)
if params[:password].blank?
params.delete :password
params.delete :password_confirmation
update_attributes params
end
end
...
end
def update
if @user.custom_update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render ''edit''
end
end
@user.username=params[:username]
if @user.update_attribute(:email,params[:email])
flash[:notice]="successful"
else
flash[:notice]="fail"
end
El código anterior puede actualizar el nombre de usuario y el campo de correo electrónico. porque update_attribute puede actualizar campos sucios. pero es una pena, update_attribute omitiría la validación.