Rails 3.2アソシエーションとPostgreSQL外部キーの微妙な違いに出くわしましたが、それを管理するための最良の方法を確実に理解したいと思います。
応答しているメッセージを追跡するMessageクラスを想定します。「from」メッセージを削除しても、チェーン内の残りのメッセージは削除されないため、:dependent => :destroy
句はありません。
class Message < ActiveRecord::Base
# from_message_id self-reference
has_many :from_messages, :foreign_key => "from_message_id",
:class_name => "Message", :inverse_of => :from_message
belongs_to :from_message,
:class_name => "Message", :inverse_of => :from_messages
attr_accessible :from_message_id, subject, body
end
ここで、2つのリンクされたメッセージを作成し、最初のメッセージを削除すると、Railsはメッセージが存在しないことを報告するだけです。
m1 = Message.create
m2 = Message.new
m2.from_message = m1
m2.save
m1.destroy
m2.from_message
=> nil
ただし、移行からデータベースに実際の外部キー制約も追加すると、次のようになります。
execute "ALTER TABLE messages ADD CONSTRAINT messages_from_message_fk
FOREIGN KEY (from_message_id) REFERENCES messages(id);"
次に、キーがまだ使用されているため、かなり厄介な中止が発生します。
m1 = Message.create
m2 = Message.new
m2.from_message = m1
m2.save
m1.destroy
=> ActiveRecord::InvalidForeignKey: PG::Error: ERROR: update or delete on table
"messages" violates foreign key constraint "messages_from_message_fk" on
table "messages"
DETAIL: Key (id)=(2) is still referenced from table "messages".
これらの結論は正しいですか?
Railsは
has_many
、およびを使用しbelongs_to
て、どのフィールドがどの外部キーを保持しているかを追跡します。「親」レコードを削除する場合、子レコードが孤立しないという検証はありません。外部キー制約のあるPostgreSQLでは、親が削除される前に、すべての子が親から「リンク解除」されている必要があります。外部キーがnull可能である限り、これは、親を削除する前に子の外部キーをnullに設定することで実現できます。
私の場合、孤児を許可したいと思います。データベースの制約を忘れたほうがいいのでしょうか、それとも、親が削除されたときにデータベースの制約を保持してレコードのリンクを解除することに価値があるのでしょうか。