web-dev-qa-db-fra.com

Ruby on Rails Valeur de retour d'enregistrement actif lorsque la création échoue?

Je suis nouveau sur Ruby on Rails et j'ai du mal à obtenir ce travail. Fondamentalement, j'ai une page d'inscription d'utilisateur qui a une confirmation de mot de passe. Dans la classe d'utilisateur I avoir la validation suivante:

validates :password, confirmation: true

Et dans le contrôleur, j'ai

def create
    vals = params[:user]
    if(User.exists(vals[:username])) 
        flash[:warning] = "#{vals[:username]} already exists! Please try a new one. "
    else
        vals[:create_date] = DateTime.current
        user = User.create(vals, :without_protection => :true)
        if user==false or user==nil or user==vals
            flash[:warning] = "#{vals[:username]} has not been registered successfully. "
        else
            flash[:notice] = "#{vals[:username]} has been registered. "
        end
    end
    redirect_to users_path
end

Le problème est que lorsque le mot de passe correspond à la confirmation, je reçois toujours le message de notification indiquant que l'enregistrement a réussi. Comme vous pouvez le voir, j'ai essayé plusieurs valeurs de retour pour create mais aucune ne semble réussir. Je suis à peu près sûr que la validation fonctionne car je ne peux pas voir l'utilisateur que je viens de créer si le mot de passe ne correspond pas à la confirmation. De plus, lorsque j'utilise create!, Je peux voir le site Web se bloquer avec l'erreur de validation. Quelqu'un peut-il m'aider à me dire ce que create doit retourner lorsque l'enregistrement n'est pas validé?

Merci.

15
zyl1024

La réponse à votre question est: User.create Renvoie une instance User si elle réussit ou échoue. S'il échoue en raison de validations, l'instance sera invalide et comportera des erreurs:

user.valid? # <= returns false
user.errors.count # <= will be > 0
user.errors.blank? # <= will be false

Votre code changerait donc de ceci:

if user==false or user==nil or user==vals

pour ça:

if !user.valid?

Vous pouvez également utiliser ce modèle:

user.attributes = vals
if user.save
   ... save succeeded ...
else
   ... save failed ...
end

La méthode save renvoie un booléen true ou false puisque vous l'appelez sur une instance existante.

Mais permet de vous mettre sur la bonne voie de plusieurs autres façons:

Premièrement: vous avez ceci:

if User.exists(vals[:username])

(Je suppose que exits est une méthode que vous mettez sur votre modèle User parce que ce n'est pas une chose Rails). Au lieu de faire cette vérification dans votre contrôleur , vous pouvez simplement utiliser une autre validation sur le modèle:

class User < ActiveRecord::Base
   ...
   validates :username, unique: true
   ...
end

Maintenant, lorsque vous essayez de créer l'utilisateur, la validation échouera si vous en avez déjà un avec ce nom.

Deuxième: Vous avez ceci:

vals[:create_date] = DateTime.current

C'est inutile. Si vous ajoutez une colonne à votre modèle appelée created_at, Elle contiendra automatiquement la date de création (gérée par ActiveRecord). Vous pouvez ajouter ceci et son partenaire updated_at À votre modèle dans votre migration comme ceci:

create_table :users do |t|
   ...
   t.timestamps # <= tells Rails to add created_at and updated_at
end

Ou, puisque vous avez déjà une table users:

add_column :users, :created_at, :datetime
add_column :users, :updated_at, :datetime

Maintenant, vous aurez toujours la date/heure de création et la dernière mise à jour de votre modèle utilisateur sans avoir besoin de code supplémentaire.

Troisièmement: Vous avez ceci:

user = User.create(vals, :without_protection => :true)

Ne fais pas ça. Au lieu de cela, changez ceci:

vals = params[:user]

Pour ça:

vals = params.require(:user).permit(:username, :password, :password_confirmation)

Et puis gardez la protection sur:

user = User.create(vals)

Vous pouvez ajouter toutes les colonnes supplémentaires que vous souhaitez apporter de votre formulaire à l'appel permit(). C'est très important car il est difficile de réparer ce genre de chose plus tard. "Si une fois que vous descendez le chemin sombre, il dominera à jamais votre destin."

Quatrième: Vous ne devez pas rediriger vers le user_path Si la sauvegarde a échoué, car il n'y aura pas de modèle utilisateur à afficher. Au lieu de cela, vous devez restituer votre formulaire new. Vous n'avez également pas besoin de messages flash pour les erreurs. Si le formulaire new s'affiche, il peut vérifier @user.errors Et signaler les messages d'erreur en conséquence. Voir la documentation d'objet d'erreur ActiveRecord .

Enfin: Vous mentionnez que votre validation échoue même lorsque votre mot de passe est correctement confirmé. Je ne peux pas dire avec certitude sans voir votre code de formulaire, mais assurez-vous que votre champ de mot de passe s'appelle password et que le champ de confirmation s'appelle password_confirmation. Rails recherche cette valeur de champ *_confirmation Spécifiquement lors de la validation pour confirmation.

Si cela ne le fait pas, postez votre code de formulaire et je réviserai.

33
gwcoffey

La réponse est ActiveRecord object. Le code source officiel montre que create retourne l'objet s'il réussit ou échoue:

# File activerecord/lib/active_record/persistence.rb, line 29
def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

Comment juger qu'il réussit ou échoue

La vraie réponse est persisted?:

if user.persisted?
  # Success
else
  # Failed
end

Pourquoi n'a pas utilisé user.valid? pour le faire? Parce que parfois, cela ne suffit pas lorsqu'il y a des rappels qui fonctionnent sur d'autres modèles:

class One < ActiveRecord::Base
  has_many :twos
  after_create :create_twos_after_create
  def create_twos_after_create
    # Use bang method in callbacks, than it will rollback while create  two failed 
    twos.create!({})    # This will fail because lack of the column `number`
  end
end

class Two < ActiveRecord::Base
  validates :number, presence: true
end

Maintenant, nous exécutons One.create échouera, vérifiez le fichier journal, ce sera un retour en arrière car il n'a pas pu en créer deux. Dans ce cas, One.create.valid? renvoie toujours true, mais sa création a échoué, utilisez donc One.create.persisted? pour le remplacer est nécessaire.

Remarque: Le code est testé dans Rails 5.1.

5
songhuangcn