web-dev-qa-db-fra.com

Rails find_or_create_by plus d'un attribut?

Il existe un attribut dynamique pratique dans active-record appelé find_or_create_by:

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

Mais que se passe-t-il si j'ai besoin de trouver ou de créer plus d'un attribut?

Supposons que j'ai un modèle pour gérer une relation M: M entre Group et Member appelée GroupMember. Je pourrais avoir de nombreuses instances où member_id = 4, mais je ne veux jamais plus d'une fois où member_id = 4 et group_id = 7. J'essaie de savoir s'il est possible de faire quelque chose comme ceci:

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

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

197
tybro0103

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

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(utilisation find_or_initialize_by _ si vous ne voulez pas sauvegarder l'enregistrement tout de suite)

Edit: La méthode ci-dessus est obsolète dans Rails 4.). La nouvelle façon de procéder sera la suivante:

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

et

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

Edit 2: Tous n'ont pas été factorisés à partir de Rails uniquement ceux spécifiques à l'attribut.

https://github.com/Rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

Exemple

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

devenu

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

Pour les personnes qui tombent sur ce fil mais qui ont besoin de rechercher ou de créer un objet avec des attributs susceptibles de changer en fonction des 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

Conseil d'optimisation: quelle que soit la solution choisie, envisagez d'ajouter des index pour les attributs que vous interrogez le plus souvent.

32
Marco

Dans Rails 4 vous pourriez 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

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

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

Au contraire, la find_or_create_by(member_id: 4, group_id: 7) appellera create sur GroupMember:

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

Veuillez voir ceci pertinent commit sur Rails/rails.

28
Juanito Fatas

En passant un bloc à find_or_create, vous pouvez transmettre des paramètres supplémentaires qui seront ajoutés à l'objet s'il est créé. Ceci est utile si vous validez la présence d'un champ dans lequel vous ne recherchez pas.

En supposant:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

ensuite

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

créera un nouveau membre du groupe avec le nom "John Doe" s'il n'en trouve pas avec member_id 4and group_id 7

15
Daniel Murphy

Tu peux 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 simplement 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
4
Dorian