質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Ruby

    8988questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails 4

    2521questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

  • PostgreSQL

    1278questions

    PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Ruby on Rails 外部キー制約によりデータが削除できません。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,497

riamk

score 34

前提・実現したいこと

Rails4 PostgreSQL

現在、railsでdeviseを使用しログイン機能を実装し、ログインユーザーがユーザー同士、個別にメッセージのやり取りができるようにしました。

具体的なテーブル構成は

usersテーブル(ユーザ情報を格納する)
conversationsテーブル(会話情報を格納する)
messagesテーブル(会話の個々のメッセージを格納する)


このようにしました。

現在のコードではメッセージをやり取りしたユーザーが退会すると外部キー制約によりデータが削除できないというエラーが発生してしまいます。

どこを修正すればいいかアドバイスをお願いします。

発生している問題・エラーメッセージ

メッセージのやり取りをしたユーザーが退会(ユーザーの削除)しようとすると以下のようなエラーが出てユーザーを削除することができません。

PG::ForeignKeyViolation at /users
ERROR:  update or delete on table "users" violates foreign key constraint "fk_rails_273a25a7a6" on table "messages"

該当のソースコード

message.rb

class Message < ActiveRecord::Base
  belongs_to :conversation
  belongs_to :user, dependent: :destroy

  validates_presence_of :body, :conversation_id, :user_id
  def message_time
    created_at.try(:strftime,"%m/%d %R")
  end
end

conversation.rb

class Conversation < ActiveRecord::Base
  belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
  belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
  has_many :messages, dependent: :destroy
  validates_uniqueness_of :sender_id, scope: :recipient_id
  scope :between, -> (sender_id,recipient_id) do
    where("(conversations.sender_id = ? AND conversations.recipient_id =?) OR (conversations.sender_id = ? AND  conversations.recipient_id =?)", sender_id,recipient_id, recipient_id, sender_id)
  end

  def target_user(current_user)
    if sender_id == current_user.id
      User.find(recipient_id)
    elsif recipient_id == current_user.id
      User.find(sender_id)
    end
  end
end

今はこのようにコードを書いています。
他に必要なコードがあれば記載しますので、ご教授よろしくお願いします。

補足・追記

Messagesテーブルのマイグレーションファイル

class CreateMessages < ActiveRecord::Migration
  def change
    create_table :messages do |t|
      t.text :body
      t.references :conversation, index: true, foreign_key: true
      t.references :user, index: true, foreign_key: true
      t.boolean :read, default: false

      t.timestamps null: false
    end
  end
end

Conversationsテーブルのマイグレーションファイル

class CreateConversations < ActiveRecord::Migration
  def change
    create_table :conversations do |t|
      t.integer :sender_id
      t.integer :recipient_id

      t.timestamps null: false
    end
  end
end
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

モデルの記述が問題と言うよりは、DBレベルでの問題な気がします。
マイグレーションファイルで、foreign_key制約はつけていますか?
どこに付けているかにも依りますが、そのあたり見せて頂けると原因が分かると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/18 09:18

    ご回答ありがとうございます。
    私のそもそもの視点が間違っていたのですね。
    マイグレーションファイルの中身を追記しましたので、何かアドバイス頂けると嬉しいです。
    よろしくお願いします。

    キャンセル

  • 2017/06/18 11:37

    `t.references :user, index: true, foreign_key: true`
    この記述が、ユーザーを削除出来ない原因かと思います。
    messagesテーブルのuser_idカラムにかけているforeign_keys制約が、ユーザーの削除を防いでいる感じですね。
    userモデルに、user has many messagesというリレーションが定義されていると思いますが、そこでdependent のオプションに delete_allとかを付けたら、解決できるかもです。
    http://blog.sanojimaru.com/post/18426241191/activerecordhasmany%E3%81%AEdependent%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3

    ただ、データの整合性を保つのがforeign_keyをつける目的なので、その意味ではOrlofskyさんが仰られているような実装方法のほうが良いかと思います。

    また、別の話になりますが、messageモデルに書かれている
    `belongs_to :user, dependent: :destroy` のdependent destroyオプションは付ける必要が無さそうですね。

    キャンセル

  • 2017/06/18 15:04

    ご教授ありがとうございます。
    Miracleさんのアドバイスを参考にもう一度foreign_keyについて学習し直してチャレンジしてみようと思います。

    messageモデルのdependentオプションは色々と試していてそのままになってしまっていました。
    ご指摘ありがとうございます。

    キャンセル

+1

messagesテーブルの対象データを先にDELETEするか、FOREIGN KEYにON DELETE CASCADE付きで外部制約を付け直されては?

実務としてはユーザーが退会したらすぐDELETEして良いものか?悪質なユーザーの再入会の回避するために、論理削除としてDELETE_FLAGを立てたり、退会済み用のテーブルに情報を残すことが多いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/18 09:21

    ご回答ありがとうございます。
    実務としてのご意見は勉強している中で触れることがない部分で、「退会済み用のテーブルに情報を残す」とかとても参考になります。
    外部制約の付け方について調べてみながら、再度トライしてみます!

    キャンセル

同じタグがついた質問を見る

  • Ruby

    8988questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails 4

    2521questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

  • PostgreSQL

    1278questions

    PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。