11
votes

Manipulation des exceptions d'enregistrement uniques dans un contrôleur

J'ai un modèle appelé abonnement qui a un index unique sur les champs [: email,: emplacement]. Cela signifie qu'une adresse e-mail peut souscrire par emplacement.

dans mon modèle: p> xxx pré>

dans ma méthode de création. Je veux gérer l'exception activerecord :: recordnotunique code> différemment d'une erreur régulière. Comment puis-je ajouter cela dans cette méthode générique Créer? P>

  def create
    @subscription = Subscription.new(params[:subscription])
    respond_to do |format|
      if @subscription.save
        format.html { redirect_to(root_url, :notice => 'Subscription was successfully created.') }
      else
        format.html { render :action => 'new' }
      end
    end
  end


0 commentaires

5 Réponses :


10
votes

Vous voudrez utiliser Rescue_from < / code>

dans votre contrôleur xxx

Cependant, ne voudriez-vous pas invalider votre dossier plutôt que de jeter une exception? < / p>


3 commentaires

Ceci est s'il est déjà trouvé dans la base de données. Pas besoin d'invalider cela, mais je suis ouvert à de meilleures suggestions. De plus, il semble que l'erreur étant lancée est en réalité `activeRecord :: recordinvalid exception: la validation a échoué: le courrier électronique a déjà été pris», pas enregistréNotunique.


Comme Dex souligne, l'exception correcte est Activerecord :: Recordinvalid.


EnregistrementInvalid est soulevé lorsque les mêmes données étaient présentes dans DB avant que ces données actuelles soient enregistrées. Ceci est la manipulation du niveau d'application Rails. RecordNotunique est soulevé lorsque l'application supposait qu'aucun enregistrement de ce type n'était présent dans dB, puis tenté de l'enregistrer, mais une autre demande a écrit exactement les mêmes données simultanément à la fois que DB a refusé de sauvegarder l'enregistrement de cette demande. C'est la manipulation de la DB de l'unicité.



18
votes

Je ne pense pas qu'il y ait un moyen d'avoir une exception projetée juste pour un seul type d'échec de validation. Soit vous pouvez faire un enregistrer! qui souleverait des exceptions pour toutes les erreurs de sauvegarde (y compris toutes les erreurs de validation) et les faire manipuler séparément.

Ce que vous pouvez faire est de gérer l'exception activeCord: : Recordinvalid et correspond au message d'exception avec Échec de la validation: Le courrier électronique a déjà été pris , puis le gérer séparément. Mais cela signifie également que vous devriez aussi gérer d'autres erreurs aussi.

quelque chose comme, xxx

Je ne sais pas si c'est la seule solution à cela cependant.


5 commentaires

Le enregistrer! est ce que j'avais manqué. Les deux travaillent, cependant, votre solution est plus minutieuse cependant. J'ai également fait une petite modification dans la ligne de sauvetage selon laquelle il est donc dit que les pairs sont examinés.


devrait être Rescue Activerecord :: Recordinvalid, Exception => E


Veuillez ne pas sauver de Exception , comme c'est la superclasse de toutes les exceptions à Ruby. Vous sauvez efficacement toutes les exceptions . Just Activerecord :: Recordinvalid fera.


Veuillez noter que si votre message est traduit par I18N ... le == ne fonctionnera pas


En fait, j'utilise activerecord :: recordnotunique à la place.



9
votes

Quelques choses que je changerais de la validation:

  1. Faites la présence, l'unicité et les validations de format dans des validations séparées. (Votre clé d'unicité dans les attributs que vous passez à «Valider» est écrasée dans votre validation). Je ferais cela ressembler plus comme:

    validateate_uniqueness_of: Email ,: Scope =>: Lieu

    validateate_presence_of: email

    validateate_format_of: e-mail ,: with => rfc_822 # Nous utilisons des regex de validation globale

  2. Les validations sont le niveau d'application, l'une des raisons pour lesquelles vous devez les séparer est que la présence et les validations de format peuvent être effectuées sans toucher la base de données. La validation de l'unicité touchera la base de données, mais n'utilisera pas l'index unique que vous configurez. Les validations de niveau d'application n'interagissent pas avec les internes de la base de données qu'ils génèrent SQL et sur la base des résultats de la requête permettent de déterminer la validité. Vous pouvez laisser le validate_uniqueness_of, mais être préparé pour les conditions de course dans votre application.

    Étant donné que la validation est le niveau d'application, il demandera la ligne (quelque chose comme "Select * à partir d'abonnements où e-mail = 'email_address' limite 1" ), si une ligne est renvoyée, la validation échoue. Si une ligne n'est pas retournée, il est considéré comme valide.

    Cependant, si quelqu'un d'autre s'inscrit avec la même adresse e-mail et qu'ils ne renvoient pas une ligne avant de créer un nouveau, le 2e "Save" commettre déclenchera la contrainte d'index de base de données d'unicité sans déclencher la validation Dans l'application. (Depuis probablement, ils fonctionnent sur différents serveurs d'applications ou au moins différents vm ou processus).

    activeCord :: Recordinvalid est soulevé lorsque la validation échoue, non lorsque la contrainte d'index unique sur la base de données est violée. (Il existe plusieurs niveaux d'exceptions ActiveRecord pouvant être déclenchés à différents moments du cycle de vie de la demande / de la réponse)

    EnregistrementInvalid est soulevé au premier niveau (niveau d'application) alors que recordnotunique peut être soulevé après la tentation de la soumission et le serveur de base de données détermine la transaction ne répond pas à la contrainte d'index. ( activeCord :: relevéinvalid est le parent de l'exception post-approvisionnement qui sera soulevé dans ce cas et que vous devriez le sauver si vous essayez réellement d'obtenir le retour de base de données et non la validation du niveau de l'application) < / p>

    Si vous êtes dans votre contrôleur "sauve_from" (comme indiqué par l'OMS) devrait fonctionner simplement pour récupérer de ces différents types d'erreurs et on dirait que l'intention initiale était de les gérer différemment. Donc, vous pouvez le faire avec plusieurs "RESCUE_DROM" .


1 commentaires

Voulez-vous ajouter que activeCord :: relevéinvalid peut arriver dans Enregistrer aussi. ActiveModel :: RangeError peut aussi se produire. Je parie qu'il y a plus de cours d'exception.



5
votes

Ajout à Chirantanes Réponse, avec rails 5 (ou 3/4, avec ce Backport ) Vous pouvez également utiliser les nouveaux erreurs errors.détails : xxx

qui est très pratique pour la différenciation entre les différents types d'enregistrement EnregistryValid types et ne nécessite pas S'appuyant sur les exceptions Erreur-Message.

Notez qu'il inclut toutes les erreurs rapportées par le processus de validation, ce qui facilite la gestion de plusieurs erreurs de validation d'une seule validation.

Par exemple, Vous pouvez vérifier si toutes les erreurs de validation pour un attribut de modèle ne sont que des erreurs d'unicité: xxx


0 commentaires

4
votes

Cette gemme sauve la défaillance de la contrainte au niveau du modèle et ajoute une erreur de modèle (modèle.Errors) afin qu'il se comporte comme d'autres défaillances de validation. Prendre plaisir! https://github.com/reverbdotcom/rescue-unique-constraint


1 commentaires

C'est bien. Merci!