Rails 一个模型中的两个多态关联
Rails two polymorphic associations in one model
我有以下模型:Game
、HighSchoolTeam
和 ClubTeam
。我想要 Game
两个有一个 team_one
和一个 team_two
字段,每个字段引用一个 HighSchoolTeam
或一个 ClubTeam
.
在 HighSchoolTeam
和 ClubTeam
我有 has_many :games, as: :teamable
。在 Game
我想做类似下面的事情...
class Game < ApplicationRecord
belongs_to :team_one, polymorphic: true, class_name: "Teamable"
belongs_to :team_two, polymorphic: true, class_name: "Teamable"
end
...但是 class_name: "Teamable
部分似乎不起作用。
编辑:
schema.rb
ActiveRecord::Schema.define(version: 2019_12_24_011346) do
...
create_table "club_teams", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "fields", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "games", force: :cascade do |t|
t.bigint "tournament_id", null: false
t.string "team_one_type", null: false
t.bigint "team_one_id", null: false
t.string "team_two_type", null: false
t.bigint "team_two_id", null: false
t.bigint "field_id", null: false
t.date "date"
t.datetime "start_time"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["field_id"], name: "index_games_on_field_id"
t.index ["team_one_type", "team_one_id"], name: "index_games_on_team_one_type_and_team_one_id"
t.index ["team_two_type", "team_two_id"], name: "index_games_on_team_two_type_and_team_two_id"
t.index ["tournament_id"], name: "index_games_on_tournament_id"
end
create_table "high_school_teams", force: :cascade do |t|
t.string "school_name"
t.string "team_name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tournaments", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "games", "fields"
add_foreign_key "games", "tournaments"
end
game.rb
class Game < ApplicationRecord
belongs_to :tournament
belongs_to :team_one, polymorphic: true
belongs_to :team_two, polymorphic: true
belongs_to :field, optional: true
end
high_school_team.rb
class HighSchoolTeam < ApplicationRecord
has_many :players
has_many :games, as: :teamable, dependent: :destroy
def name
self.school_name
end
end
club_team.rb
class ClubTeam < ApplicationRecord
has_many :players
has_many :games, as: :teamable, dependent: :destroy
end
控制台输出
code/scout-db [master●] » rails c --sandbox
Running via Spring preloader in process 48525
Loading development environment in sandbox (Rails 6.0.1)
Any modifications you make will be rolled back on exit
WARNING: This version of ruby is included in macOS for compatibility with legacy software.
In future versions of macOS the ruby runtime will not be available by
default, and may require you to install an additional package.
irb(main):001:0> game = Game.new({ team_one_id: "high-school-team-2", team_one_type: "HighSchoolTeam", team_two_id: "club-team-2", team_two_type: "ClubTeam" })
(0.2ms) BEGIN
=> #<Game id: nil, tournament_id: nil, team_one_type: "HighSchoolTeam", team_one_id: 0, team_two_type: "ClubTeam", team_two_id: 0, field_id: nil, date: nil, start_time: nil, created_at: nil, updated_at: nil>
irb(main):002:0> game.team_one_id
=> 0
irb(main):003:0> game.save
(0.3ms) SAVEPOINT active_record_1
HighSchoolTeam Load (0.4ms) SELECT "high_school_teams".* FROM "high_school_teams" WHERE "high_school_teams"."id" = LIMIT [["id", 0], ["LIMIT", 1]]
ClubTeam Load (0.3ms) SELECT "club_teams".* FROM "club_teams" WHERE "club_teams"."id" = LIMIT [["id", 0], ["LIMIT", 1]]
(0.4ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
irb(main):004:0> game.errors.full_messages.inspect
=> "[\"Tournament must exist\", \"Team one must exist\", \"Team two must exist\"]"
(2, Syosset, Braves, 2019-12-31 01:07:41.367913, 2019-12-31 01:07:41.367913)
存在于 high_school_teams
table 并且 (2, Foobars, 2019-12-31 01:07:52.697821, 2019-12-31 01:07:52.697821)
存在于 club_teams
table.
当然 class_name: "Teamable"
不起作用,因为多态关联的全部要点是 class 即 class(更重要的是目标 table)该协会是动态的。也不需要。
多态关联使用单独的 association_name_type
字符串列,其中包含与其关联的 class 名称。
给定以下数据:
| id | team_one_id | team_one_type # ...
----------------------------------
1 | 1 | "HighSchoolTeam"
2 | 2 | "ClubTeam"
3 | 3 | "HighSchoolTeam"
当你做Game.find(1).team_one
时,Rails知道使用HighSchoolTeam
class并加入high_school_teams
table。但是它需要在建立连接之前拉出行,当然你的数据库对关系一无所知,也不会保持参照完整性。
所以你只需要:
class Game < ApplicationRecord
belongs_to :team_one, polymorphic: true
belongs_to :team_two, polymorphic: true
end
并确保在 games
table.
上有 team_one_type
和 team_two_type
字符串列
class_name
应该与数据库中的 class 名称匹配。
我有以下模型:Game
、HighSchoolTeam
和 ClubTeam
。我想要 Game
两个有一个 team_one
和一个 team_two
字段,每个字段引用一个 HighSchoolTeam
或一个 ClubTeam
.
在 HighSchoolTeam
和 ClubTeam
我有 has_many :games, as: :teamable
。在 Game
我想做类似下面的事情...
class Game < ApplicationRecord
belongs_to :team_one, polymorphic: true, class_name: "Teamable"
belongs_to :team_two, polymorphic: true, class_name: "Teamable"
end
...但是 class_name: "Teamable
部分似乎不起作用。
编辑:
schema.rb
ActiveRecord::Schema.define(version: 2019_12_24_011346) do
...
create_table "club_teams", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "fields", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "games", force: :cascade do |t|
t.bigint "tournament_id", null: false
t.string "team_one_type", null: false
t.bigint "team_one_id", null: false
t.string "team_two_type", null: false
t.bigint "team_two_id", null: false
t.bigint "field_id", null: false
t.date "date"
t.datetime "start_time"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["field_id"], name: "index_games_on_field_id"
t.index ["team_one_type", "team_one_id"], name: "index_games_on_team_one_type_and_team_one_id"
t.index ["team_two_type", "team_two_id"], name: "index_games_on_team_two_type_and_team_two_id"
t.index ["tournament_id"], name: "index_games_on_tournament_id"
end
create_table "high_school_teams", force: :cascade do |t|
t.string "school_name"
t.string "team_name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tournaments", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "games", "fields"
add_foreign_key "games", "tournaments"
end
game.rb
class Game < ApplicationRecord
belongs_to :tournament
belongs_to :team_one, polymorphic: true
belongs_to :team_two, polymorphic: true
belongs_to :field, optional: true
end
high_school_team.rb
class HighSchoolTeam < ApplicationRecord
has_many :players
has_many :games, as: :teamable, dependent: :destroy
def name
self.school_name
end
end
club_team.rb
class ClubTeam < ApplicationRecord
has_many :players
has_many :games, as: :teamable, dependent: :destroy
end
控制台输出
code/scout-db [master●] » rails c --sandbox
Running via Spring preloader in process 48525
Loading development environment in sandbox (Rails 6.0.1)
Any modifications you make will be rolled back on exit
WARNING: This version of ruby is included in macOS for compatibility with legacy software.
In future versions of macOS the ruby runtime will not be available by
default, and may require you to install an additional package.
irb(main):001:0> game = Game.new({ team_one_id: "high-school-team-2", team_one_type: "HighSchoolTeam", team_two_id: "club-team-2", team_two_type: "ClubTeam" })
(0.2ms) BEGIN
=> #<Game id: nil, tournament_id: nil, team_one_type: "HighSchoolTeam", team_one_id: 0, team_two_type: "ClubTeam", team_two_id: 0, field_id: nil, date: nil, start_time: nil, created_at: nil, updated_at: nil>
irb(main):002:0> game.team_one_id
=> 0
irb(main):003:0> game.save
(0.3ms) SAVEPOINT active_record_1
HighSchoolTeam Load (0.4ms) SELECT "high_school_teams".* FROM "high_school_teams" WHERE "high_school_teams"."id" = LIMIT [["id", 0], ["LIMIT", 1]]
ClubTeam Load (0.3ms) SELECT "club_teams".* FROM "club_teams" WHERE "club_teams"."id" = LIMIT [["id", 0], ["LIMIT", 1]]
(0.4ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
irb(main):004:0> game.errors.full_messages.inspect
=> "[\"Tournament must exist\", \"Team one must exist\", \"Team two must exist\"]"
(2, Syosset, Braves, 2019-12-31 01:07:41.367913, 2019-12-31 01:07:41.367913)
存在于 high_school_teams
table 并且 (2, Foobars, 2019-12-31 01:07:52.697821, 2019-12-31 01:07:52.697821)
存在于 club_teams
table.
当然 class_name: "Teamable"
不起作用,因为多态关联的全部要点是 class 即 class(更重要的是目标 table)该协会是动态的。也不需要。
多态关联使用单独的 association_name_type
字符串列,其中包含与其关联的 class 名称。
给定以下数据:
| id | team_one_id | team_one_type # ...
----------------------------------
1 | 1 | "HighSchoolTeam"
2 | 2 | "ClubTeam"
3 | 3 | "HighSchoolTeam"
当你做Game.find(1).team_one
时,Rails知道使用HighSchoolTeam
class并加入high_school_teams
table。但是它需要在建立连接之前拉出行,当然你的数据库对关系一无所知,也不会保持参照完整性。
所以你只需要:
class Game < ApplicationRecord
belongs_to :team_one, polymorphic: true
belongs_to :team_two, polymorphic: true
end
并确保在 games
table.
team_one_type
和 team_two_type
字符串列
class_name
应该与数据库中的 class 名称匹配。