Rails find_or_create_by более одного атрибута? - PullRequest
196 голосов
/ 15 июня 2010

В активной записи есть удобный динамический атрибут с именем find_or_create_by:

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

Но что, если мне нужно найти find_or_create более чем одним атрибутом?

Допустим, у меня есть модель для обработки отношений M: M между Группой и участником, которая называется GroupMember. У меня может быть много случаев, когда member_id = 4, но я никогда не хочу более одного раза, когда member_id = 4 и group_id = 7. Я пытаюсь выяснить, возможно ли сделать что-то подобное:

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

Я понимаю, что могут быть лучшие способы справиться с этим, но мне нравится удобство идеи find_or_create.

Ответы [ 5 ]

458 голосов
/ 15 июня 2010

Несколько атрибутов могут быть связаны с and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(используйте find_or_initialize_by, если вы не хотите сразу сохранять запись)

Редактировать: Вышеупомянутый метод устарел в Rails 4. Новый способ сделать это будет:

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

и

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

Редактировать 2: Не все из них были выделены из рельсов, а только из атрибутов.

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

Пример

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

стало

GroupMember.find_or_create_by(member_id: 4, group_id: 7)
32 голосов
/ 27 мая 2012

Для тех, кто сталкивается с этим потоком, но ему нужно найти или создать объект с атрибутами, которые могут изменяться в зависимости от обстоятельств, добавьте в вашу модель следующий метод:

# 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

Совет по оптимизации: независимо от того, какое решение вы выберете, рассмотрите возможность добавления индексов для наиболее часто запрашиваемых атрибутов.

27 голосов
/ 04 января 2014

В Rails 4 вы можете сделать:

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

И использование where отличается:

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

Это вызовет create на GroupMember.where(member_id: 4, group_id: 7):

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

Напротив, find_or_create_by(member_id: 4, group_id: 7) будет вызывать create на GroupMember:

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

Пожалуйста, смотрите этот соответствующий коммит на рельсах / рельсах.

15 голосов
/ 19 июня 2013

Передав блок в find_or_create, вы можете передать дополнительные параметры, которые будут добавлены к объекту, если он будет создан новым. Это полезно, если вы проверяете наличие поля, по которому вы не ищете.

Предполагая, что:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

тогда

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

создаст новый член группы с именем «Джон Доу», если он не найдет его с member_id 4 and group_id 7

4 голосов
/ 11 августа 2014

Вы можете сделать:

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

Или просто инициализировать:

User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...