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
คุณเข้าใจความคิดเห็นในเอกสารประกอบหรือไม่: "สำหรับการเชื่อมโยงของไอเท็มเพื่อเป็นของสมาคม และเอกสารยังใช้ตัวอย่างที่แน่นอน ฉันหายไปนี่อะไร
dynex

50
ทั้งหมดนี้แปลกมากสำหรับฉันเพราะดูเหมือนว่าคุณจะต้องการพฤติกรรมนี้โดยค่าเริ่มต้นและจะต้องใช้เท่านั้น: inverse_of เมื่อไม่สามารถอนุมานชื่อสมาคมได้ ความไม่สอดคล้องกันในคำจำกัดความนั้นน่ารำคาญ แต่มันก็ช่วยฉันในบางกรณี เหตุผลใดที่ฉันไม่ควรติดมันทุกที่?
อิบราฮิม

18
@Ibrahim ลองดูสิมันถูกรวมเข้ากับ 23 วันแล้ว! github.com/rails/rails/pull/9522
Hut8

6
มันสมเหตุสมผลสำหรับการผกผันของการเชื่อมโยง92ที่จะถูกละเว้นเพราะลูกของเรคคอร์ดหลักของเร็กคอร์ด A ไม่ได้รับประกันว่าจะเป็นเรคคอร์ด A - อาจเป็นพี่น้องของเรคคอร์ด A ผู้ปกครองของเรคคอร์ด A อย่างไรก็ตาม มีการรับประกันว่าจะบันทึก A.
David Aldridge

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
ฉันมีปัญหากับ accepts_nested_attributes_for โดยค่าเริ่มต้นเฉพาะแอตทริบิวต์ที่ซ้อนกันสำหรับวัตถุที่เกี่ยวข้องที่มีอยู่แสดง (แก้ไขการกระทำ) ตัวอย่างเช่นหากคุณต้องการสร้างวัตถุด้วยพูดว่าวัตถุที่เกี่ยวข้อง 3 รายการคุณควรมี Model.new (การกระทำใหม่) และ: inverse_of ในแบบจำลองของคุณ
Victor Marconi

เห็นด้วยกับพฤติกรรมใน Rails 4 และใหม่กว่า แต่ทำงานได้ดีใน v3 (ยกเว้นบางสาขาในภายหลังแม้ว่าไวยากรณ์เก่าจะทำงานได้อีกใน v3.2.13) และหมายเหตุในรุ่นเข้าร่วมไม่สามารถตรวจสอบการมีอยู่ของ id ได้อีก - เฉพาะโมเดลวัตถุ ดูเหมือนว่าคุณสามารถมีการเชื่อมโยงโดยไม่มี ID สำหรับมันใน v4 'logic'
JosephK

แน่นอน .. :inverse_ofแก้ปัญหาสำหรับฉันเมื่อสร้างเอนทิตีหลักและลูกย่อยในรูปแบบเดียวกัน
WM

16

หลังจาก pr ( https://github.com/rails/rails/pull/9522 ) นี้ไม่จำเป็นต้องใช้inverse_ofในกรณีส่วนใหญ่

Active Record รองรับการระบุอัตโนมัติสำหรับการเชื่อมโยงส่วนใหญ่ที่มีชื่อมาตรฐาน อย่างไรก็ตาม Active Record จะไม่ระบุความสัมพันธ์แบบสองทิศทางโดยอัตโนมัติที่มีขอบเขตหรือตัวเลือกใด ๆ ต่อไปนี้:

  • : ผ่าน
  • : 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

ในตัวอย่างข้างต้นอ้างอิงถึงวัตถุเดียวกันถูกเก็บไว้ในตัวแปรและแอตทริบิวต์awriter


ฉันใช้ Rails 5 ไม่ว่าคุณจะเพิ่มinverse_ofหรือไม่ก็ตามผลลัพธ์สำหรับa.first_name == b.author.first_nameมันก็น่าเบื่ออยู่เสมอ
Arslan Ali

@ArslanAli ขอบคุณสำหรับความคิดเห็นที่ดีฉันปรับปรุงคำตอบ
artamonovdev

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 และเป็นความสัมพันธ์แบบ _ มันจะดีกว่าเสมอที่จะใช้ inverse_of ซึ่งแจ้งให้ ActiveRecod ทราบว่าเป็นรุ่นเดียวกันกับสมาคม ดังนั้นหากมีการเรียกใช้คิวรีจากด้านใดด้านหนึ่งก็จะแคชและให้บริการจากแคชหากถูกทริกเกอร์จากทิศทางตรงกันข้าม ซึ่งช่วยเพิ่มประสิทธิภาพ จาก Rails 4.1, inverse_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:

ไม่ทำงานกับ: ผ่านการเชื่อมโยง

ไม่ทำงานกับ: การเชื่อมโยง polymorphic

สำหรับ owner_to ความสัมพันธ์ has_many ความสัมพันธ์ที่ตรงกันข้ามจะถูกละเว้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.