Rails Guides 说“如果您需要验证、回调或连接模型上的额外属性,您应该使用 has_many :through”。正如伟大的编码哲学家 Avi Flombaum 曾经逃避的那样,你怎么可能知道你的连接模型在申请过程的早期不会起到额外的作用。无论您处于开发阶段的哪个阶段,您永远无法看到未来这么远知道您不需要扩展连接表。
Why You Don’t Need Has_and_belongs_to_many Relationships
顺便说一句:
# models/firm.rb
class Firm < ActiveRecord::Base
has_many :clients
has_many :cases, through: :clients
end
# models/client.rb
class Client < ActiveRecord::Base
belongs_to :firm
has_many :client_cases
has_many :cases, through: :client_cases
end
# models/case.rb
class Case < ActiveRecord::Base
has_many :client_cases
has_many :clients, through: :client_cases
end
# models/client_case.rb
class ClientCase < ActiveRecord::Base
belongs_to :client
belongs_to :case
end
更新
您的问题必须在其他地方;我能够在终端中创建以下内容而没有任何错误。
f = Firm.create name: 'Magical Group' # => #<Firm id: 1...
b = f.clients.create name: 'Billy' # => #<Client id: 1...
m = f.clients.create name: 'Megan' # => #<Client id: 2...
c = Case.create name: 'Billy & Megan are getting married!' # => #<Case id: 1...
b.cases << c # => #<ActiveRecord::Associations::CollectionProxy
m.cases << c # => #<ActiveRecord::Associations::CollectionProxy
f.cases.count # => 2
f.cases.uniq.count # => 1
我已经提供了数据库架构,请确保您的类似于:
ActiveRecord::Schema.define(version: 20150813173900) do
create_table "cases", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "client_cases", force: :cascade do |t|
t.integer "client_id"
t.integer "case_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "client_cases", ["case_id"], name: "index_client_cases_on_case_id"
add_index "client_cases", ["client_id"], name: "index_client_cases_on_client_id"
create_table "clients", force: :cascade do |t|
t.string "name"
t.integer "firm_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "clients", ["firm_id"], name: "index_clients_on_firm_id"
create_table "firms", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end