3

このエラーを追跡するのにしばらく時間がかかりましたが、最終的に理由がわかりました。Rails フレームワークを使用してカード ゲームをモデリングしています。現在、私のデータベースは(ほとんど)次のようになっています。

cards     cards_games     games      
-----     -----------     -----
id        id              id
c_type    card_id         ...
value     game_id         other_stuff

そして、Rails ActiveRecord card.rb と game.rb は現在、次のようになっています。

#card.rb
class Card < ActiveRecord::Base
  has_and_belongs_to_many :player
  has_and_belongs_to_many :game
  has_and_belongs_to_many :cardsInPlay, :class_name => "Rule"
end

#game.rb
class Game < ActiveRecord::Base
  has_and_belongs_to_many :cards
  has_many :players
  has_one :rules, :class_name => Rule
end

ゲームを実行しようとすると、複数のゲーム (1 つ以上) があると、エラーが発生します

ActiveRecord::StatementInvalid in GameController#start_game
# example
Mysql::Error: Duplicate entry '31' for key 1: INSERT INTO `cards_games` (`card_id`, `id`, `game_id`) VALUES (31, 31, 7)

アクションが失敗するたびに、cardid == id. これには、Rails がデータをデータベースに挿入する方法に何らかの問題があると思います。cardsgames オブジェクトがないので、card_id を id にプルしてデータベースに挿入しているだけだと思います。これは、cardgames の主キー制約に違反する、同じカードを使用する 2 つのゲームができるまで問題なく機能します。データベースに精通している私は、この問題に対する最初の解決策は、id を削除して cardid と gameid を主キーにすることで、Rails がこの関係の「実際の」定義に従うようにすることでした。移行が2つの主キーを持つことを処理できなかったように見えるため、機能しませんでした(Rails APIがそれをしても大丈夫だと言っているにもかかわらず..奇妙です)。これに対する別の解決策は、「id」を省略することです INSERT INTO ステートメントの列を変更し、データベースに自動インクリメントを処理させます。残念ながら、これを行う方法もわかりません。

それで、これに対する別の回避策はありますか?私が知らない気の利いたRailsのトリックはありますか? それとも、この種の構造は Rails では不可能ですか? 何が悪いのか、いくつかの修正方法を知っいるので、これは本当にイライラしますが、Rail フレームワークの制約により、それを行うことができません。

4

5 に答える 5

10

has_and_belongs_to_manyid主キー列を持たない結合テーブルを意味します。移行を次のように変更します

create_table :cards_games, :id => false do ...

マットが指摘したように。2 つの列からキーを作成した場合にのみよく眠れる場合は、それらに一意のインデックスを作成します。

add_index :cards_games, [ :card_id, :game_id ], :unique => true

さらに、名前付けが Rails の慣例から逸脱しているため、コードが少し読みにくくなります。

has_and_belongs_to_manyクラスのインスタンスを見るときの 1:M 関係を定義します。ではCard、次を使用する必要があります。

has_and_belongs_to_many :players
has_and_belongs_to_many :games

複数の「プレイヤー」と「ゲーム」に注意してください。同様にGame

has_one :rule

これにより、不要な も削除:class_name => Ruleできます。

于 2009-06-09T15:51:20.640 に答える
4

ID 列を削除するには、最初から作成しないでください。

  create_table :cards_rules, :id => false do ...
于 2009-06-04T22:35:07.677 に答える
1

Dr.Nicsの複合主キーを参照してください

http://compositekeys.rubyforge.org/

于 2009-06-04T22:18:34.823 に答える
0

途中でハッキングした後、解決策を見つけました。移行内で「実行」機能を使用できることがわかりました。これは非常に有用であり、この問題に対する洗練されていない解決策をまとめることができました。より洗練された、より Rails に似たソリューションをお持ちの場合は、お知らせください。移行の形でのソリューションは次のとおりです。

class Make < ActiveRecord::Migration
  def self.up
    drop_table :cards_games
    create_table :cards_games do |t|
      t.column :card_id, :integer, :null => false
      t.column :game_id, :integer, :null => false
    end
    execute "ALTER TABLE cards_games DROP COLUMN id"
    execute "ALTER TABLE cards_games ADD PRIMARY KEY (card_id, game_id)"

    drop_table :cards_players
    create_table :cards_players do |t|
      t.column :card_id, :integer, :null => false
      t.column :player_id, :integer, :null => false
    end
    execute "ALTER TABLE cards_players DROP COLUMN id"
    execute "ALTER TABLE cards_players ADD PRIMARY KEY (card_id, player_id)"

    drop_table :cards_rules
    create_table :cards_rules do |t|
      t.column :card_id, :integer, :null => false
      t.column :rule_id, :integer, :null => false
    end
    execute "ALTER TABLE cards_rules DROP COLUMN id"
    execute "ALTER TABLE cards_rules ADD PRIMARY KEY (card_id, rule_id)"
  end

  def self.down
    drop_table :cards_games
    create_table :cards_games do |t|
      t.column :card_id, :integer
      t.column :game_id, :integer
    end

    drop_table :cards_players
    create_table :cards_players do |t|
      t.column :card_id, :integer
      t.column :player_id, :integer
    end

    drop_table :cards_rules
    create_table :cards_rules do |t|
      t.column :card_id, :integer
      t.column :rule_id, :integer
    end
  end
end
于 2009-03-26T10:34:05.020 に答える
0

このforeign_key_migrationsプラグインをチェックしてみてください

于 2009-03-26T10:38:40.237 に答える