Hiểu biết: tùy chọn nguồn của has_one / has_many thông qua Rails


184

Xin hãy giúp tôi hiểu các :sourcetùy chọn của has_one/has_many :throughhiệp hội. Giải thích API Rails có ý nghĩa rất nhỏ đối với tôi.

"Chỉ định tên liên kết nguồn được sử dụng bởi has_many :through => :queries. Chỉ sử dụng tên đó nếu tên đó không thể được suy ra từ liên kết. has_many :subscribers, :through => :subscriptionsSẽ tìm kiếm :subscribershoặc :subscriberbật Subscription, trừ khi :sourceđược đưa ra."

Câu trả lời:


237

Đôi khi, bạn muốn sử dụng các tên khác nhau cho các hiệp hội khác nhau. Nếu tên bạn muốn sử dụng cho liên kết trên mô hình không giống với tên trên :throughmô hình, bạn có thể sử dụng :sourceđể chỉ định tên đó.

Tôi không nghĩ rằng đoạn trên là nhiều rõ ràng hơn so với một trong các tài liệu, vì vậy đây là một ví dụ. Giả sử chúng ta có ba mô hình Pet, DogDog::Breed.

class Pet < ActiveRecord::Base
  has_many :dogs
end

class Dog < ActiveRecord::Base
  belongs_to :pet
  has_many :breeds
end

class Dog::Breed < ActiveRecord::Base
  belongs_to :dog
end

Trong trường hợp này, chúng tôi đã chọn không gian tên Dog::Breed, bởi vì chúng tôi muốn truy cập Dog.find(123).breedsnhư một hiệp hội tốt và thuận tiện.

Bây giờ, nếu bây giờ chúng tôi muốn tạo một has_many :dog_breeds, :through => :dogshiệp hội trên Pet, chúng tôi đột nhiên có một vấn đề. Rails sẽ không thể tìm được một :dog_breedshiệp hội trên Dog, vì vậy Rails không thể nào biết được Dog sự liên kết bạn muốn sử dụng. Nhập :source:

class Pet < ActiveRecord::Base
  has_many :dogs
  has_many :dog_breeds, :through => :dogs, :source => :breeds
end

Với :source, chúng tôi đang nói với Rails tìm kiếm một hiệp hội được gọi :breedstrên Dogmô hình (vì đó là mô hình được sử dụng cho :dogs) và sử dụng nó.


2
Tôi nghĩ rằng bạn có nghĩa là lớp động vật cuối cùng của bạn được gọi là lớp Pet, chỉ là một lỗi đánh máy tôi tin.
Kamilski81

3
Trong ví dụ trên, hiệp hội dưới Dognên has_many :breedthay vì :breedsvà sau đó :source:breedsố ít, để đại diện cho tên mô hình, thay vì :breedsđại diện cho tên bảng? Ví dụ has_many :dog_breeds, :through => :dogs, :source => :breed(không có shậu tố :breed)?
LazerSharks

1
Tôi đã thử nghiệm điều này. nó là số ít, không có shậu tố trong:source =>
Anwar

"Trong trường hợp này, chúng tôi đã chọn không gian tên Dog :: Breed, vì chúng tôi muốn truy cập Dog.find (123) .breeds như một hiệp hội tốt đẹp và thuận tiện.". Bạn không cần một không gian tên cho điều đó phải không?
Jwan622

200

Hãy để tôi mở rộng trên ví dụ đó:

class User
  has_many :subscriptions
  has_many :newsletters, :through => :subscriptions
end

class Newsletter
  has_many :subscriptions
  has_many :users, :through => :subscriptions
end

class Subscription
  belongs_to :newsletter
  belongs_to :user
end

Với mã này, bạn có thể làm một cái gì đó như Newsletter.find(id).usersđể có được danh sách những người đăng ký bản tin. Nhưng nếu bạn muốn rõ ràng hơn và có thể nhập Newsletter.find(id).subscribersthay thế, bạn phải thay đổi lớp Bản tin thành này:

class Newsletter
  has_many :subscriptions
  has_many :subscribers, :through => :subscriptions, :source => :user
end

Bạn đang đổi tên usershiệp hội thành subscribers. Nếu bạn không cung cấp :source, Rails sẽ tìm kiếm một hiệp hội được gọi subscribertrong lớp Đăng ký. Bạn phải bảo nó sử dụng userliên kết trong lớp Đăng ký để tạo danh sách người đăng ký.


2
lưu ý rằng, tên mô hình số ít nên được sử dụng :source =>, không phải số nhiều. Vì vậy, :userslà sai, :userlà chính xác
Anwar

Đây là câu trả lời hay nhất!, Tôi chỉ nhấn mạnh phần này: "Bạn đang đổi tên hiệp hội người dùng thành người đăng ký. Nếu bạn không cung cấp: nguồn, Rails sẽ tìm kiếm một hiệp hội được gọi là thuê bao trong lớp Đăng ký."
Brian Joseph Spinos

11

Câu trả lời đơn giản nhất:

Là tên của mối quan hệ trong bảng ở giữa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.