O que inverse_of faz? Qual SQL ele gera?

143

Estou tentando entender inverse_ofe não entendo.

Como é o sql gerado, se houver?

Será que a inverse_ofopção de apresentar o mesmo comportamento se usado com :has_many, :belongs_toe :has_many_and_belongs_to?

Desculpe se esta é uma pergunta tão básica.

Eu vi este exemplo:

class Player < ActiveRecord::Base
  has_many :cards, :inverse_of => :player
end

class Card < ActiveRecord::Base
  belongs_to :player, :inverse_of => :cards
end
Federico
fonte

Respostas:

125

A partir da documentação , parece que a :inverse_ofopção é um método para evitar consultas SQL, não gerá-las. É uma dica para o ActiveRecord usar dados já carregados em vez de buscá-los novamente por meio de um relacionamento.

O exemplo deles:

class Dungeon < ActiveRecord::Base
  has_many :traps, :inverse_of => :dungeon
  has_one :evil_wizard, :inverse_of => :dungeon
end

class Trap < ActiveRecord::Base
  belongs_to :dungeon, :inverse_of => :traps
end

class EvilWizard < ActiveRecord::Base
  belongs_to :dungeon, :inverse_of => :evil_wizard
end

Nesse caso, a chamada dungeon.traps.first.dungeondeve retornar o dungeonobjeto original em vez de carregar um novo, como seria o caso por padrão.

tadman
fonte
5
Você entendeu o comentário na documentação: "para associações pertence a associações has_many muitas associações inversas são ignoradas.". E, no entanto, o documento usa esse exemplo exato. O que estou perdendo aqui?
Dynex
50
Isso tudo é muito estranho para mim, porque parece-me que você sempre desejaria esse comportamento por padrão e só precisa usar: inverso_de quando o nome da associação não puder ser inferido. Além disso, as inconsistências na definição são incômodas, mas isso me ajudou em alguns casos. Alguma razão para eu não colocar isso em todo lugar?
Ibrahim
18
@Ibrahim Confira, foi mesclado há 23 dias! github.com/rails/rails/pull/9522
Hut8
6
Faz sentido que a inversa de uma associação pertence_ a ser ignorada porque não é garantido que o filho do registro A de um registro pai seja A - poderia ser um irmão do Registro A. No entanto, o pai de um filho do registro A , é garantido que seja o registro A.
David Aldridge
2
Futuro leitor pode obter ajuda a partir deste blogue ...: D
Arup Rakshit
42

Eu acho que :inverse_ofé mais útil quando você está trabalhando com associações que ainda não foram persistidas. Por exemplo:

class Project < ActiveRecord::Base
  has_many :tasks, :inverse_of=>:project
end

class Task < ActiveRecord::Base
  belongs_to :project, :inverse_of=>:tasks
end

Agora, no console:

irb> p = Project.new
=> #<Project id: nil, name: nil, ...>
irb> t = p.tasks.build
=> #<Task id: nil, project_id: nil, ...>
irb> t.project
=> #<Project id: nil, name: nil, ...>

Sem os :inverse_ofargumentos, t.projectretornaria nil, porque aciona uma consulta sql e os dados ainda não estão armazenados. Com os :inverse_ofargumentos, os dados são recuperados da memória.

KenB
fonte
1
Eu tive um problema com accept_nested_attributes_for. Por padrão, apenas atributos aninhados para objetos associados existentes são exibidos (ação de edição). Se, por exemplo, você quiser CRIAR um objeto com, digamos, 3 objetos associados, deverá ter Model.new (nova ação) e: inverse_of em seus modelos.
Victor Marconi
Concordou com o comportamento no Rails 4 e posterior, mas funcionou bem na v3 (exceto em algumas encarnações posteriores, embora a sintaxe antiga funcione novamente na v3.2.13). E observe que no modelo de junção, não é mais possível validar a presença dos IDs - apenas o objeto de modelo. Parece que você pode ter uma associação sem um ID para ela, na v4 'lógica'.
JosephK
Exatamente .. :inverse_ofresolveu um problema para mim ao criar novas entidades pai e filho da mesma forma.
WM
16

Após este procedimento ( https://github.com/rails/rails/pull/9522 ), inverse_of não é necessário na maioria dos casos.

O Active Record suporta identificação automática para a maioria das associações com nomes padrão. No entanto, o Active Record não identifica automaticamente associações bidirecionais que contêm um escopo ou qualquer uma das seguintes opções:

  • :através
  • : Foreign_key
class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => true

No exemplo acima, uma referência ao mesmo objecto é armazenado na variável ae no atributo writer.

artamonovdev
fonte
Estou usando o Rails 5 e, se você adiciona inverse_ofou não, o resultado a.first_name == b.author.first_nameé sempre ture.
Arslan Ali
@ArslanAli obrigado pelo ótimo comentário, atualizei a resposta.
Artemonovdev 18/11/19
5

Apenas uma atualização para todos - usamos apenas inverse_ofum de nossos aplicativos com uma has_many :throughassociação


Basicamente, torna o objeto "origem" disponível para o objeto "filho"

Então, se você estiver usando o exemplo do Rails:

class Dungeon < ActiveRecord::Base
  has_many :traps, :inverse_of => :dungeon
  has_one :evil_wizard, :inverse_of => :dungeon
end

class Trap < ActiveRecord::Base
  belongs_to :dungeon, :inverse_of => :traps
  validates :id,
      :presence => { :message => "Dungeon ID Required", :unless => :draft? }

  private
  def draft?
      self.dungeon.draft
  end 
end

class EvilWizard < ActiveRecord::Base
  belongs_to :dungeon, :inverse_of => :evil_wizard
end

O uso :inverse_ofpermitirá acessar o objeto de dados do qual é inverso, sem executar nenhuma consulta SQL adicional

Richard Peck
fonte
5

Quando temos dois modelos com relacionamento has_many e pertence_ a, é sempre melhor usar o inverso_ que informa o ActiveRecod que eles pertencem ao mesmo lado da associação. Portanto, se uma consulta de um lado for acionada, ela será armazenada em cache e servida a partir do cache se for acionada na direção oposta. O que melhora o desempenho. No Rails 4.1, inverse_of será definido automaticamente, se usarmos Foreign_key ou alterações no nome da classe, precisaremos definir explicitamente.

Melhor artigo para detalhes e exemplo.

http://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations

Gurudath BN
fonte
3

Se você possui uma has_many_throughrelação entre dois modelos, Usuário e Função, e deseja validar a Atribuição do modelo de conexão com entradas inexistentes ou inválidas validates_presence of :user_id, :role_id, é útil. Você ainda pode gerar um usuário @ user com sua associação, @user.role(params[:role_id])para que o salvamento do usuário não resulte em uma falha na validação do modelo de atribuição.

Informatom
fonte
-1

Por favor, dê uma olhada 2 dois recursos úteis

E lembre-se de algumas limitações de inverse_of:

não funciona com: por meio de associações.

não funciona com: associações polimórficas.

para associações belongs_to has_many associações inversas são ignoradas.

Leo Le
fonte