Rails znaleźć lub utworzyć przez więcej niż jeden atrybut?
W active-record znajduje się przydatny atrybut dynamiczny o nazwie find_or_create_by:
Model.find_or_create_by_<attribute>(:<attribute> => "")
Ale co jeśli muszę find_or_create przez więcej niż jeden atrybut?
Powiedzmy, że mam model do obsługi relacji M: M między grupą a członkiem zwanym GroupMember. Mogę mieć wiele instancji, w których member_id = 4, ale nigdy nie chcę więcej niż raz instancji, w których member_id = 4 i group_id = 7. Zastanawiam się, czy można zrobić coś takiego. to:
GroupMember.find_or_create(:member_id => 4, :group_id => 7)
Zdaję sobie sprawę, że mogą być lepsze sposoby, aby sobie z tym poradzić, ale podoba mi się wygoda idei find_or_create.
5 answers
Wiele atrybutów można połączyć z and
:
GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
(użyj find_or_initialize_by
Jeśli nie chcesz od razu zapisać rekordu)
Edit: powyższa metoda jest przestarzała w Rails 4. Nowy sposób na to będzie:
GroupMember.where(:member_id => 4, :group_id => 7).first_or_create
I
GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize
Edit 2: nie wszystkie z nich były brane pod uwagę z rails tylko te atrybuty specyficzne.
Https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md
Przykład
GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
Stał się
GroupMember.find_or_create_by(member_id: 4, group_id: 7)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-17 12:26:06
Dla każdego, kto natknie się na ten wątek, ale musi znaleźć lub utworzyć obiekt z atrybutami, które mogą się zmienić w zależności od okoliczności, dodaj następującą metodę do modelu:
# 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
Porada optymalizacyjna: niezależnie od tego, które rozwiązanie wybierzesz, rozważ dodanie indeksów dla atrybutów, o które pytasz najczęściej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-26 21:00:42
W Rails 4 możesz zrobić:
GroupMember.find_or_create_by(member_id: 4, group_id: 7)
I użycie where
jest inne:
GroupMember.where(member_id: 4, group_id: 7).first_or_create
To wywoła create
na GroupMember.where(member_id: 4, group_id: 7)
:
GroupMember.where(member_id: 4, group_id: 7).create
Przeciwnie, find_or_create_by(member_id: 4, group_id: 7)
wywoła create
na GroupMember
:
GroupMember.create(member_id: 4, group_id: 7)
Zobacz ten odpowiedni commit on rails / rails.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 02:37:51
Przekazując blok do find_or_create
, można przekazać dodatkowe parametry, które zostaną dodane do obiektu, jeśli zostanie utworzony nowy. Jest to przydatne, jeśli sprawdzasz obecność pola, którego nie wyszukujesz.
Zakładając:
class GroupMember < ActiveRecord::Base
validates_presence_of :name
end
Then
GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }
Utworzy nowego członka grupy o nazwie "John Doe", jeśli nie znajdzie go z member_id 4
and group_id 7
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 14:01:27
Możesz zrobić:
User.find_or_create_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_create
Lub po prostu zainicjować:
User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-11 19:54:15