Подтвердить что ты не робот

Rails find_or_create более чем одним атрибутом?

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

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

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

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

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

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

4b9b3361

Ответ 1

Несколько атрибутов могут быть связаны с 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)

Ответ 2

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

# 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

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

Ответ 3

В 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)

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

Ответ 4

Пройдя блок до 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" }

создаст новый GroupMember с именем "John Doe", если он не найдет один с member_id 4 and group_id 7

Ответ 5

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

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