206 votes

Rails find_or_create par plus d'un attribut?

Il y a une pratique dynamique de l'attribut active record appelé find_or_create_by:

Model.find_or_create_by_<attribute>(:<attribute> => "")

Mais que faire si j'ai besoin de find_or_create par plus d'un attribut?

Dire que j'ai un modèle pour gérer un M:M la relation entre le Groupe et Membre appelé GroupMember. J'ai pu avoir de nombreux cas où member_id = 4, mais je ne voulez pas jamais plus d'une fois l'instance où member_id = 4 et group_id = 7. Je suis en train de voir si c'est possible de faire quelque chose comme ceci:

GroupMember.find_or_create(:member_id => 4, :group_id => 7)

Je me rends compte il peut y avoir de meilleures façons de gérer cela, mais j'aime la commodité de l'idée de find_or_create.

474voto

x1a4 Points 14082

Plusieurs attributs peuvent être connectés avec un and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(utiliser find_or_initialize_by si vous ne voulez pas l'enregistrer tout de suite)

Edit: La méthode ci-dessus est déconseillée dans les Rails 4. La nouvelle façon de faire, ce sera:

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create

et

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize

34voto

Marco Points 1719

Pour quelqu'un d'autre qui bute sur ce fil, mais a besoin de trouver ou de créer un objet avec des attributs susceptibles de changer selon les circonstances, ajoutez la méthode suivante à votre modèle:

# Return the first object which matches the attributes hash
# - or -
# Create new object with the given attributes
#
def self.find_or_create(attributes)
  Model.where(attributes).first || Model.create(attributes)
end

L'optimisation de la pointe: peu importe quelle solution vous choisissez, pensez à ajouter des index pour les attributs que vous interrogez plus fréquemment.

33voto

juanitofatas Points 1651

Dans les Rails 4 que vous pouvez faire:

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

Et utiliser where est différent:

GroupMember.where(member_id: 4, group_id: 7).first_or_create

Cela appellera create sur GroupMember.where(member_id: 4, group_id: 7):

GroupMember.where(member_id: 4, group_id: 7).create

Au contraire, l' find_or_create_by(member_id: 4, group_id: 7) appellerons create sur GroupMember:

GroupMember.create(member_id: 4, group_id: 7)

Veuillez voir ce pertinent de s'engager sur des rails/rails.

18voto

Daniel Murphy Points 61

Par le passage d'un bloc à l' find_or_create, vous pouvez passer des paramètres supplémentaires qui seront ajoutés à l'objet, si elle est créée de nouveau. Ceci est utile si vous êtes à la validation de la présence d'un champ que vous n'êtes pas à la recherche par.

En supposant que:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

alors

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }

va créer un nouveau GroupMember avec le nom "John Doe" si il ne trouve pas l'une avec l' member_id 4 and group_id 7

4voto

Dorian Points 2384

Vous pouvez le faire:

User.find_or_create_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_create

Ou à initialiser:

User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X