2013-08-09 15 views
34

Tôi đang làm việc với một điều kiện khá đơn giản: tình huống mà tôi có thể làm cho tham số class_name/external_key hoạt động theo một hướng nhưng không phải là thông số khác. Có lẽ bạn có thể giúp tôi. (p.s. Tôi đang sử dụng Rails 4 nếu điều đó tạo ra sự khác biệt):has_many: thông qua với class_name và foreign_key

Tiếng Anh: Người dùng quản lý nhiều Danh sách thông qua Trình quản lý danh sách và Danh sách được quản lý bởi nhiều Người dùng thông qua Trình quản lý danh sách. Danh sách quản lý có một số lĩnh vực dữ liệu, không Gecman cho câu hỏi này, vì vậy tôi sửa chúng ra trong đoạn code dưới đây

Đây là phần đơn giản mà hoạt động:

class User < ActiveRecord::Base 
    has_many :listing_managers 
    has_many :listings, through: :listing_managers 
end 

class Listing < ActiveRecord::Base 
    has_many :listing_managers 
    has_many :managers, through: :listing_managers, class_name: "User", foreign_key: "manager_id" 
end 

class ListingManager < ActiveRecord::Base 
    belongs_to :listing 
    belongs_to :manager, class_name:"User" 

    attr_accessible :listing_id, :manager_id 
end 

như bạn có thể đoán từ phía trên bảng vẻ ListingManager như:

create_table "listing_managers", force: true do |t| 
    t.integer "listing_id" 
    t.integer "manager_id" 
end 

nên chỉ không đơn giản ở đây là sử dụng ListingManager manager_id hơn user_id

Dù sao, các công trình trên. Tôi có thể gọi số user.listings để có được các Danh sách được liên kết với người dùng và tôi có thể gọi số listing.managers để yêu cầu người quản lý liên kết với danh sách.

Tuy nhiên (và đây là câu hỏi), tôi quyết định đó không phải là khủng khiếp có ý nghĩa để nói user.listings kể từ khi một người sử dụng cũng có thể "sở hữu" hơn là "quản lý" danh sách, những gì tôi thực sự muốn là user.managed_listings vì vậy tôi chỉnh user.rb thay đổi has_many: danh sách, thông qua:: listing_managers để has_many: managed_listings, thông qua:: listing_managers, class_name: "Liệt kê", foreign_key: "listing_id"

Đây là một loại suy chính xác để mã trong listing.rb trên, vì vậy tôi nghĩ rằng điều này sẽ làm việc ngay lập tức. Thay vì kiểm tra rspec tôi điều này bằng cách nói barfs ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :managed_listing or :managed_listings in model ListingManager. Try 'has_many :managed_listings, :through => :listing_managers, :source => <name>'. Is it one of :listing or :manager?

kiểm tra phúc:

it "manages many managed_listings" do 
    user = FactoryGirl.build(:user) 
    l1 = FactoryGirl.build(:listing) 
    l2 = FactoryGirl.build(:listing)  
    user.managed_listings << l1 
    user.managed_listings << l2 
    expect(@user.managed_listings.size).to eq 2 
end 

Bây giờ, tôi bị thuyết phục tôi không biết gì. Có, tôi đoán tôi có thể làm một bí danh, nhưng tôi lo lắng rằng cùng một kỹ thuật được sử dụng trong listing.rb dường như không hoạt động trong user.rb. Bạn có thể giúp giải thích?

CẬP NHẬT: Tôi đã cập nhật mã để phản ánh @gregates đề xuất, nhưng tôi vẫn gặp sự cố: Tôi đã viết một kiểm tra bổ sung không thành công (và được xác nhận bằng "tay" trong bảng điều khiển Rails). Khi một người viết bài kiểm tra như thế này:

it "manages many managed_listings" do 
    l1 = FactoryGirl.create(:listing) 
    @user = User.last 
    ListingManager.destroy_all 
    @before_count = ListingManager.count 
    expect( @before_count).to eq 0 
    lm = FactoryGirl.create(:listing_manager, manager_id: @user.id, listing_id: l1.id) 


    expect(@user.managed_listings.count).to eq 1 
end 

Ở trên không thành công. Đường ray tạo lỗi PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist (Cần tìm kiếm 'listing_managers.manager_id'). Vì vậy, tôi nghĩ rằng vẫn còn một lỗi về phía người dùng của hiệp hội. Trong số của user.rb, Người dùng biết cách sử dụng manager_id để truy cập vào (các) Danh sách của nó như thế nào?

Trả lời

41

Vấn đề ở đây là trong

has_many :managers, through: :listing_managers 

ActiveRecord có thể suy ra rằng tên của hiệp hội trên mô hình tham gia (: listing_managers) bởi vì nó có cùng tên như hiệp hội has_many :through bạn đang xác định .Tức là, cả hai danh sách và listing_mangers đều có nhiều người quản lý .

Nhưng đó không phải là trường hợp trong liên kết khác của bạn. Có, một listing_manager has_many :listings, nhưng một người dùng has_many :managed_listings. Vì vậy, ActiveRecord không thể suy ra tên của hiệp hội trên ListingManager mà nó nên sử dụng.

Đây là tùy chọn :source dành cho (xem http://guides.rubyonrails.org/association_basics.html#has-many-association-reference). Vì vậy, việc kê khai đúng sẽ là:

has_many :managed_listings, through: :listing_managers, source: :listing

(ps bạn không thực sự cần :foreign_key hoặc :class_name tùy chọn trên has_many :through khác Bạn muốn sử dụng để xác định những trực tiếp hiệp hội, và sau đó tất cả các bạn. cần phải có một số has_many :through để trỏ đến liên kết chính xác trên mô hình :through.)

+0

thú vị ... và tôi đã thử 'nguồn:" Danh sách "" nhưng điều đó không hiệu quả. Vì vậy, biểu tượng là cách duy nhất để thiết lập nó. – user2669055

+0

Tôi sẽ đánh dấu điều này là đã trả lời (và cảm ơn vì tốc độ!) Mặc dù tôi vẫn còn hơi mờ về vấn đề nguồn: - Tôi đã dành rất nhiều thời gian với Hướng dẫn bạn đề cập, nhưng tài liệu cho nguồn là rất VERY ánh sáng. – user2669055

+0

Điểm then chốt mà tôi nghĩ là ở trang p.s. Tôi đã thêm. Đối với một hiệp hội * thông qua * một mô hình tham gia, bạn chỉ cần nói cho nó tên của hiệp hội bạn đang tham gia thông qua (nếu nó không thể được suy ra). Phần còn lại của các tùy chọn không quan trọng. Đó là những liên kết * trực tiếp *. Và đó là tất cả 'nguồn', chỉ định liên kết để sử dụng trên mô hình kết hợp cho một liên kết' thông qua'. – gregates

4

Tôi biết đây là một câu hỏi cũ, nhưng tôi đã dành thời gian chạy vào cùng một lỗi và cuối cùng đã tìm ra. Đây là những gì tôi đã làm:

class User < ActiveRecord::Base 
    has_many :listing_managers 
    has_many :managed_listings, through: :listing_managers, source: :listing 
end 

class Listing < ActiveRecord::Base 
    has_many :listing_managers 
    has_many :managers, through: :listing_managers, source: :user 
end 

class ListingManager < ActiveRecord::Base 
    belongs_to :listing 
    belongs_to :user 
end 

Đây là những gì ListingManager tham gia bảng trông giống như:

create_table :listing_managers do |t| 
    t.integer :listing_id 
    t.integer :user_id 
end 

Hope this helps người tìm kiếm trong tương lai.