Що робить Inverse_of? Який SQL він генерує?


143

Я намагаюся обвести голову, inverse_ofі мені це не вдається.

Як виглядає створений sql, якщо такий є?

Чи має inverse_ofвиставляти опції таку ж поведінку , якщо використовується :has_many, :belongs_toі :has_many_and_belongs_to?

Вибачте, якщо це таке основне питання.

Я бачив цей приклад:

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

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

Відповіді:


125

З документації , схоже, що:inverse_of варіант - це спосіб уникнути запитів SQL, а не їх генерувати. ActiveRecord - це підказка використовувати вже завантажені дані, а не отримувати їх знову через зв’язок.

Їх приклад:

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

У цьому випадку виклик dungeon.traps.first.dungeonповинен повернути початковий dungeonоб'єкт замість завантаження нового, як це було б за замовчуванням.


5
Чи розумієте ви коментар у документації: "для pripadaних асоціацій has_many зворотні асоціації ігноруються." І все-таки doc використовує саме той приклад. Що я тут пропускаю?
дінекс

50
Це все дуже дивно для мене, тому що мені здається, що ти завжди хотів би цю поведінку за замовчуванням, і потрібно використовувати лише: inverse_of, коли ім'я асоціації не можна зробити. Також невідповідності у визначенні набридають, але це мені допомогло в декількох випадках. З якоїсь причини я не повинен просто скрізь її дотримуватися?
Ібрагім

18
@Ibrahim Перевірте це, його було об'єднано 23 дні тому! github.com/rails/rails/pull/9522
Хата8

6
Має сенс ігнорувати інверсію асоціації pripadaн_то, оскільки дитина батьківського запису запису A не гарантується як запис A - це може бути побратим запису A. Батько дитини, що має запис A, однак , гарантовано буде записаний А.
Девід Олдрідж

2
Майбутній читач може отримати допомогу в цьому щоденнику ...: D
Arup Rakshit

42

Я думаю :inverse_of, що найкорисніше, коли ви працюєте з асоціаціями, які ще не існували. Наприклад:

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

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

Тепер у консолі:

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, ...>

Без :inverse_ofаргументів t.projectповернеться nil, оскільки він запускає запит sql, а дані ще не зберігаються. За допомогою :inverse_ofаргументів дані витягуються з пам'яті.


1
У мене виникла проблема з acceptts_nested_attributes_for. За замовчуванням відображаються лише вкладені атрибути для існуючих пов'язаних об’єктів (редагування дії). Якщо, наприклад, ви хочете створити об’єкт, скажімо, з 3 пов’язаними об’єктами, у ваших моделях має бути Model.new (нова дія) та: inverse_of.
Віктор Марконі

Погодився з поведінкою в Rails 4 та пізніших версіях, але він працював чудово у версії v3 (за винятком деяких пізніших втілень, хоча старий синтаксис працює знову в версії 3.23). І зверніть увагу, що в моделі приєднання, вже не може перевірити наявність ідентифікатора - лише модель-об'єкт. Здається, ви можете мати асоціацію без ідентифікатора для неї в v4 "логіці".
JosephK

Точно .. :inverse_ofвирішив для мене питання при створенні нових батьківських та дочірніх утворень у тій же формі.
WM

16

Після цього PR ( https://github.com/rails/rails/pull/9522 ) inverse_of в більшості випадків не потрібен.

Active Record підтримує автоматичну ідентифікацію для більшості асоціацій зі стандартними іменами. Однак Active Record не автоматично визначає двонаправлені асоціації, що містять область дії чи будь-який із наступних варіантів:

  • : наскрізь
  • :зовнішній ключ
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

У наведеному вище прикладі посилання на один і той же об'єкт зберігається у змінній aта в атрибуті writer.


Я використовую Rails 5, і якщо ви додаєте inverse_ofчи ні, результат для a.first_name == b.author.first_nameцього завжди впевнений.
Арслан Алі

@ArslanAli дякую за чудовий коментар, я оновив відповідь.
артамоновдев

5

Просто оновлення для всіх - ми просто використовували inverse_ofодин із наших додатків із has_many :throughасоціацією


Це в основному робить об'єкт "походження" доступним для "дочірнього" об'єкта

Отже, якщо ви використовуєте приклад 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

Використання :inverse_ofдозволить вам отримати доступ до об'єкта даних, для якого це обернено, без виконання додаткових SQL-запитів


5

Коли у нас є 2 моделі з співвідношеннями has_many і pripadaться_so, завжди краще використовувати зворотний_of, який повідомляє ActiveRecod, що вони належать до тієї ж сторони асоціації. Отже, якщо запускається запит з однієї сторони, він кешуватиме та керуватиме з кешу, якщо він буде запущений з протилежного напрямку. Що покращує продуктивність. З Rails 4.1 інверсія_of буде встановлена ​​автоматично, якщо ми використовуємо Foreign_key або зміни в назві класу, які нам потрібно встановити явно.

Найкраща стаття для деталей та прикладу.

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



3

Якщо ви маєте has_many_throughвідношення між двома моделями, Користувачем та Роллю, і хочете перевірити приєднану модель Призначення щодо неіснуючих або недійсних записів validates_presence of :user_id, :role_id, це корисно. Ви все ще можете створити User @user з його асоціацією, @user.role(params[:role_id])щоб збереження користувача не призвело до невдалої перевірки моделі призначення.


-1

Погляньте, будь-ласка, два корисні ресурси

І пам’ятайте про деякі обмеження inverse_of:

не працює з: через асоціації.

не працює з: поліморфними асоціаціями.

для належень до асоціацій has_many зворотні асоціації ігноруються.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.