web-dev-qa-db-fra.com

Rails vérifiant si un enregistrement existe dans la base de données

Quel est le moyen le plus efficace de vérifier si une base de données retournera un enregistrement avant de le traiter? Exemple: Truck.where("id = ?", id).select('truck_no').first.truck_no

Cela peut ou non renvoyer un camion si le camion existe. Quel est le moyen le plus efficace pour moi d’assurer que la page ne plantera pas lors du traitement de cette demande. Comment pourrais-je gérer cela à la fois dans la vue et dans le contrôleur si, disons, j'utilisais une boucle pour parcourir chaque camion et imprimer son numéro.

Si le document n'existe pas, j'aimerais pouvoir imprimer un message au lieu de dire qu'aucun document n'a été trouvé.

44
Bojan

Si vous voulez vérifier l'existence d'un objet, pourquoi ne pas utiliser existe?

if Truck.exists?(10)
  # your truck exists in the database
else
  # the truck doesn't exist
end

Le exists? La méthode présente l'avantage de ne pas sélectionner l'enregistrement dans la base de données (la signification est plus rapide que de sélectionner l'enregistrement). La requête ressemble à:

SELECT 1 FROM trucks where trucks.id = 10

Vous pouvez trouver plus d'exemples dans le documentation Rails pour #exists? .

94
cristian

Voici comment vous pouvez vérifier cela.

if Trucks.where(:id => current_truck.id).blank?
  # no truck record for this id
else
  # at least 1 record for this truck
end

la méthode retourne un objet ActiveRecord :: Relation (agit comme un tableau contenant les résultats de où), il peut être vide mais jamais nul.

35
Shaunak

OP solution de cas d'utilisation réelle

La solution la plus simple consiste à combiner la vérification de votre base de données et la récupération des données dans une requête à une base de données au lieu d’avoir des appels de base de données distincts. Votre exemple de code est proche et traduit votre intention, mais sa syntaxe est légèrement différente.

Si vous faites simplement Truck.where("id = ?", id).select('truck_no').first.truck_no et que cet enregistrement n'existe PAS, il générera une erreur nil lorsque vous appelez truck_no Car first peut extraire un nil record si aucun ne correspond à vos critères.

En effet, votre requête retournera un tableau d'objets correspondant à vos critères. Ensuite, vous effectuez un first sur ce tableau qui (si aucun enregistrement correspondant n'est trouvé) est nil.

Une solution assez propre:

# Note: using Rails 4 / Ruby 2 syntax
first_truck = Truck.select(:truck_no).find_by(id) # => <Truck id: nil, truck_no: "123"> OR nil if no record matches criteria

if first_truck
  truck_number = first_truck.truck_no
  # do some processing...
else
  # record does not exist with that criteria
end

Je recommande d'utiliser une syntaxe propre qui "se" commente afin que les autres sachent exactement ce que vous essayez de faire.

Si vous voulez vraiment faire un effort supplémentaire, vous pouvez ajouter une méthode à votre classe Truck qui le fait pour vous et traduit votre intention:

# truck.rb model
class Truck < ActiveRecord::Base
  def self.truck_number_if_exists(record_id)
    record = Truck.select(:truck_no).find_by(record_id)
    if record
      record.truck_no
    else
      nil # explicit nil so other developers know exactly what's going on
    end
  end
end

Alors tu l'appellerais comme ceci:

if truck_number = Truck.truck_number_if_exists(id)
  # do processing because record exists and you have the value
else
  # no matching criteria
end

La méthode ActiveRecord.find_by Récupérera le premier enregistrement correspondant à vos critères ou retournera nil si aucun enregistrement n’est trouvé avec ce critère. Notez que l'ordre des méthodes find_by Et where est important; vous devez appeler le select du modèle Truck. En effet, lorsque vous appelez la méthode where, vous retournez en fait un objet ActiveRelation qui n’est pas ce que vous recherchez ici.

Voir API ActiveRecord pour la méthode 'find_by'

Des solutions générales utilisant 'existe?' méthode

Comme certains des autres contributeurs l'ont déjà mentionné, la méthode exists? Est conçue spécifiquement pour vérifier l'existence de quelque chose. Il ne renvoie pas la valeur, mais confirme que la base de données a un enregistrement qui correspond à certains critères.

C'est utile si vous devez vérifier l'unicité ou l'exactitude de certaines données. La partie intéressante est qu’elle vous permet d’utiliser les critères ActiveRelation(Record?) where(...).

Par exemple, si vous avez un modèle User avec un attribut email et que vous devez vérifier si un courrier électronique existe déjà dans le dB:

User.exists?(email: "[email protected]")

L'avantage d'utiliser exists? Est que l'exécution de la requête SQL est

SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1

ce qui est plus efficace que de renvoyer des données.

Si vous avez besoin d'extraire conditionnellement des données de la base de données, ce n'est pas la méthode à utiliser. Cependant, cela fonctionne très bien pour une vérification simple et la syntaxe est très claire afin que les autres développeurs sachent exactement ce que vous faites. L'utilisation de la syntaxe appropriée est essentielle dans les projets avec plusieurs développeurs. Écrivez le code propre et laissez le code "commenter" lui-même.

8
Dan L

Vous pouvez juste faire:

@truck_no = Truck.where("id = ?", id).pluck(:truck_no).first

Ceci retournera nil si aucun enregistrement n'est trouvé, ou truck_no de seulement le premier enregistrez autrement.

Ensuite, à votre avis, vous pouvez faire quelque chose comme:

<%= @truck_no || "There are no truck numbers" %>

Si vous voulez récupérer et afficher plusieurs résultats, alors dans votre contrôleur:

@truck_nos = Truck.where("id = ?", id).pluck(:truck_no)

et à votre avis:

<% truck_nos.each do |truck_no| %>
  <%= truck_no %>
<% end %>

<%= "No truck numbers to iterate" if truck_nos.blank? %>
2
Agis

Si vous voulez juste vérifier si l’enregistrement existe ou non. Allez avec la réponse de @ cristian i.e.

Truck.exists?(truck_id) # returns true or false

Mais si les camions sortent, vous voulez accéder à ce camion, puis vous devrez le retrouver, ce qui entraînera deux interrogations dans la base de données. Si c'est le cas, allez avec

@trcuk = Truck.find_by(id: truck_id) #returns nil or truck
@truck.nil? #returns true if no truck is db
@truck.present? #returns true if no truck is db
0
Imran