【问题标题】:Rails two polymorphic associations in one model在一个模型中 Rails 两个多态关联
【发布时间】:2020-01-02 20:52:31
【问题描述】:

我有以下型号:GameHighSchoolTeamClubTeam。我想要Game 两个有一个team_one 和一个team_two 字段,每个字段都引用HighSchoolTeamClubTeam

HighSchoolTeamClubTeam 我有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" = $1 LIMIT $2  [["id", 0], ["LIMIT", 1]]
  ClubTeam Load (0.3ms)  SELECT "club_teams".* FROM "club_teams" WHERE "club_teams"."id" = $1 LIMIT $2  [["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 表中,(2, Foobars, 2019-12-31 01:07:52.697821, 2019-12-31 01:07:52.697821) 存在于club_teams 表中。

【问题讨论】:

    标签: ruby-on-rails associations polymorphic-associations


    【解决方案1】:

    当然class_name: "Teamable" 不起作用,因为多态关联的全部意义在于关联的类(更重要的是目标表)是动态的。它也不需要。

    多态关联使用单独的association_name_type 字符串列,其中包含与其关联的类名。

    给定以下数据:

    | id | team_one_id | team_one_type # ...
    ----------------------------------
    1    |  1          | "HighSchoolTeam"
    2    |  2          | "ClubTeam"
    3    |  3          | "HighSchoolTeam"
    

    当您执行Game.find(1).team_one 时,Rails 知道使用HighSchoolTeam 类并加入high_school_teams 表。但它需要在建立连接之前拉出行,当然您的数据库对关系一无所知,也不会保持引用完整性。

    所以你只需要:

    class Game < ApplicationRecord
      belongs_to :team_one, polymorphic: true
      belongs_to :team_two, polymorphic: true
    end
    

    并确保games 表中有team_one_typeteam_two_type 字符串列。

    【讨论】:

    • 如果这实际上是单表继承设置,则不需要使用多态关联。
    • 我在games 表中有team_one_typeteam_two_type 字符串列(以及team_one_idteam_two_id),而belongs_to :team_one, polymorphic: true 代码不起作用。我将用详细信息编辑问题。
    • 没关系,我想通了。我使用例如high-school-team-2team_one_id,而它应该只是2
    • 但是如何遍历团队的所有gamesas: teamable 在哪里发挥作用?!
    • @felix 你必须做Game.where('team_one_id = :id OR team_two_id = :id AND team_one_type = :type OR team_two_type: :type', id: team.id, type: team.class.name)as: 选项在关联的has_many 一侧起作用。它告诉 Rails 关联是多态的,以及在关联的另一端调用什么 this 模型。
    【解决方案2】:

    class_name 应该与数据库中的类名匹配。

    【讨论】:

    • 有道理。因此,鉴于我们在这里处理的是类可能引用HighSchoolTeamClubTeam 的多态关系,我们应该怎么做?
    • 这个答案与多态关联有什么关系?
    猜你喜欢
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多