Existe um atributo dinâmico útil no registro ativo chamado find_or_create_by:
Model.find_or_create_by_<attribute>(:<attribute> => "")
Mas e se eu precisar localizar ou criar mais de um atributo?
Digamos que eu tenha um modelo para lidar com um relacionamento M: M entre Grupo e Membro chamado GroupMember. Eu poderia ter muitas instâncias em que member_id = 4, mas nunca mais quero uma instância em que member_id = 4 e group_id = 7. Estou tentando descobrir se é possível fazer algo assim:
GroupMember.find_or_create(:member_id => 4, :group_id => 7)
Sei que pode haver maneiras melhores de lidar com isso, mas gosto da conveniência da idéia de find_or_create.
Para qualquer pessoa que se depara com esse encadeamento, mas precisa localizar ou criar um objeto com atributos que possam mudar dependendo das circunstâncias, adicione o seguinte método ao seu modelo:
Dica de otimização: independentemente de qual solução você escolher, considere adicionar índices para os atributos que você está consultando com mais frequência.
fonte
find_or_create_by
fará.Model.where(attributes).instance_eval{|q| q.first || q.create}
.find_or_create
também tem a vantagem de usar uma transação, em quewhere….first || create
introduz uma condição de corridadef self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) end
para que você não precisa repetirModel
self
do corpo do método (já que deseja remover as palavras!).No Rails 4, você poderia fazer:
E o uso
where
é diferente:Isso vai chamar
create
emGroupMember.where(member_id: 4, group_id: 7)
:Pelo contrário, o
find_or_create_by(member_id: 4, group_id: 7)
chamarácreate
emGroupMember
:Por favor, veja este commit relevante nos trilhos / trilhos.
fonte
Ao passar um bloco para
find_or_create
, você pode passar parâmetros adicionais que serão adicionados ao objeto se ele for criado novo. Isso é útil se você estiver validando a presença de um campo que não está pesquisando.Assumindo:
então
criará um novo GroupMember com o nome "John Doe" se não encontrar um com
member_id 4
and group_id 7
fonte
Você pode fazer:
Ou apenas para inicializar:
fonte