web-dev-qa-db-fra.com

Comment validez-vous l'unicité d'une paire d'identifiants dans Ruby on Rails?

Supposons la migration de base de données suivante dans Ruby:

 create_table: question_votes do | t | 
 t.integer: user_id 
 t.integer: question_id 
 t.integer: vote 
 
 t. horodatages 
 fin 

Supposons en outre que je souhaite que les lignes de la base de données contiennent des paires uniques (user_id, question_id). Quelle est la bonne poussière à mettre dans le modèle pour y parvenir?

validates_uniqueness_of: user_id,: question_id
50
dfrankow
validates_uniqueness_of :user_id, :scope => [:question_id]

si vous devez inclure une autre colonne (ou plus), vous pouvez également l'ajouter à la portée. Exemple:

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
89
Demi

Si vous utilisez mysql, vous pouvez le faire dans la base de données en utilisant un index unique. C'est quelque chose comme:

add_index :question_votes, [:question_id, :user_id], :unique => true

Cela va déclencher une exception lorsque vous essayez d'enregistrer une combinaison doublée de question_id/user_id, vous devrez donc expérimenter et déterminer quelle exception intercepter et gérer.

25
pakeha

De RailsGuides . validates fonctionne aussi:

class QuestionVote < ActiveRecord::Base
  validates :user_id, :uniqueness => { :scope => :question_id }
end
16
Jonathan Lin

La meilleure façon est d'utiliser les deux, car Rails n'est pas fiable à 100% lorsque la validation de l'unicité passe par.

Vous pouvez utiliser:

  validates :user_id, uniqueness: { scope: :question_id }

et pour être sûr à 100%, ajoutez cette validation sur votre db (ex MySQL)

  add_index :question_votes, [:user_id, :question_id], unique: true

puis vous pouvez gérer votre contrôleur en utilisant:

  rescue ActiveRecord::RecordNotUnique

Alors maintenant, vous êtes sûr à 100% que vous n'aurez pas de valeur en double :)

14
dpedoneze

Sauf pour écrire votre propre méthode de validation, le mieux que vous puissiez faire avec validates_uniqueness_of est-ce:

validates_uniqueness_of :user_id, :scope => "question_id"

Cela vérifiera que le user_id est unique dans toutes les lignes avec le même question_id que l'enregistrement que vous essayez d'insérer.

Mais ce n'est pas ce que vous voulez.

Je pense que vous recherchez la combinaison de :user_id et :question_id pour être unique dans la base de données.

Dans ce cas, vous devez faire deux choses:

  1. Écrivez votre propre méthode de validation.
  2. Créez une contrainte dans la base de données car il est possible que votre application traite deux enregistrements en même temps.
10
eggdrop

Lorsque vous créez un nouvel enregistrement, cela ne fonctionne pas car le id de votre modèle parent n'existe pas encore au moment des validations.

Cela devrait fonctionner pour vous.

class B < ActiveRecord::Base
  has_many :ab
  has_many :a, :through => :ab
end

class AB < ActiveRecord::Base
  belongs_to :b
  belongs_to :a
end

class A < ActiveRecord::Base
  has_many :ab
  has_many :b, :through => :ab

  after_validation :validate_uniqueness_b

  private
  def validate_uniqueness_b
    b_ids = ab.map(&:b_id)
    unless b_ids.uniq.length.eql? b_ids.length
      errors.add(:db, message: "no repeat b's")
    end
  end
end

Dans le code ci-dessus, je reçois tous les b_id de la collection de paramètres, puis comparer si la longueur entre les valeurs uniques et b_id obtenues sont égales.
Si sont égaux signifie qu'il n'y a pas de répétition b_id.

Remarque: n'oubliez pas d'ajouter unique dans les colonnes de votre base de données.

2
Francisco Balam