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.
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)
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.
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.
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 4
and group_id 7
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