Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord::StatementInvalid: Mysql2::Error: Cannot delete or update a parent row - Rails 4.2.6

Error that need to be solved:

ActiveRecord::StatementInvalid: Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (slap_chat_development.chatrooms, CONSTRAINT fk_rails_496733c195 FOREIGN KEY (group_id) REFERENCES groups (id)): DELETE FROM groups WHERE groups.id = 1

QUESTION IS:

  • can someone guide me where from should this error be fixed.

As I see problem persists in relation between groups and chatrooms tables.

Further details:

schema.rb

ActiveRecord::Schema.define(version: 20160606100750) do

  create_table "chatrooms", force: :cascade do |t|
    t.integer  "group_id",   limit: 4
    t.string   "name",       limit: 255
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
  end

  add_index "chatrooms", ["group_id"], name: "index_chatrooms_on_group_id", using: :btree

  create_table "chatrooms_users", force: :cascade do |t|
    t.integer  "chatroom_id", limit: 4
    t.integer  "user_id",     limit: 4
    t.datetime "created_at",            null: false
    t.datetime "updated_at",            null: false
  end

  add_index "chatrooms_users", ["chatroom_id"], name: "index_chatrooms_users_on_chatroom_id", using: :btree
  add_index "chatrooms_users", ["user_id"], name: "index_chatrooms_users_on_user_id", using: :btree

  create_table "groups", force: :cascade do |t|
    t.string   "name",       limit: 255
    t.integer  "user_id",    limit: 4
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
  end

  add_index "groups", ["user_id"], name: "index_groups_on_user_id", using: :btree

  create_table "groups_users", force: :cascade do |t|
    t.integer  "group_id",   limit: 4
    t.integer  "user_id",    limit: 4
    t.datetime "created_at",           null: false
    t.datetime "updated_at",           null: false
  end

  add_index "groups_users", ["group_id"], name: "index_groups_users_on_group_id", using: :btree
  add_index "groups_users", ["user_id"], name: "index_groups_users_on_user_id", using: :btree

  create_table "posts", force: :cascade do |t|
    t.integer  "user_id",    limit: 4
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
    t.string   "content",    limit: 255
  end

  add_index "posts", ["user_id"], name: "index_posts_on_user_id", using: :btree

  create_table "users", force: :cascade do |t|
    t.string   "email",                  limit: 255, default: "", null: false
    t.string   "encrypted_password",     limit: 255, default: "", null: false
    t.string   "reset_password_token",   limit: 255
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          limit: 4,   default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip",     limit: 255
    t.string   "last_sign_in_ip",        limit: 255
    t.datetime "created_at",                                      null: false
    t.datetime "updated_at",                                      null: false
    t.string   "first_name",             limit: 255
    t.string   "nick_name",              limit: 255
    t.string   "last_name",              limit: 255
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree

  add_foreign_key "chatrooms", "groups"
  add_foreign_key "chatrooms_users", "chatrooms"
  add_foreign_key "chatrooms_users", "users"
  add_foreign_key "groups", "users"
  add_foreign_key "groups_users", "groups"
  add_foreign_key "groups_users", "users"
  add_foreign_key "posts", "users"
end

Group model:

class Group < ActiveRecord::Base
  has_many :chatrooms
  belongs_to :group_admin, class_name: "User", foreign_key: :user_id
  has_and_belongs_to_many :members, class_name: "User", association_foreign_key: :user_id

  validates :user_id, presence: true
  validates :name, presence: true, length: { minimum: 3 }

  before_create { self.name = self.name.capitalize }
  after_create :assign_creator
  around_destroy :destroy_all_associates

  def general_room
    self.chatrooms.where("name = ?", "general").take
  end

  def except_general_room
    self.chatrooms.select { |room| room != self.general_room }
  end

  def assign_creator
    member = self.group_admin
    self.members << member
    self.general_room.members << member
  end

  def destroy_all_associates
    rooms = self.chatrooms
    yield
    rooms.each do |room|
        room.destroy
    end
  end
end

Chatroom model:

class Chatroom < ActiveRecord::Base
  belongs_to :group
  has_and_belongs_to_many :members, class_name: "User", association_foreign_key: :user_id

  validates :name, presence: true, length: { minimum: 3 }

  before_save { self.name = self.name.downcase }
  around_create :ensure_group_presence
  around_destroy :destroy_all_associates

  def feed
    ids = Array.new
    self.members.each do |member|
        ids += member.post_ids
    end
    Post.where("id IN (?)", ids)
  end

  def ensure_group_presence
    yield
    self.group_id.present?
  end

  def destroy_all_associates
    feed = self.feed
    yield
    feed.destroy_all
  end
end
like image 264
Andrius Avatar asked Jun 06 '16 18:06

Andrius


3 Answers

You can try using this in your group model.

class Group < ActiveRecord::Base
  has_many :chatrooms , dependent: :destroy
end

Now when you execute, Group.last.destroy, it should delete the dependent associated chatrooms before and no hanging data would be left

like image 188
Bijendra Avatar answered Nov 19 '22 15:11

Bijendra


It seems like you are trying to delete a group that has one or many chatrooms.

But because you added a foreign key constraint (add_foreign_key "chatrooms", "groups"), it is not allowed to delete a group when there is still a chatroom assigned.

To solve this issue you have to destroy all associated chatrooms before destroying the group itself.

like image 45
spickermann Avatar answered Nov 19 '22 14:11

spickermann


I think it depends on whether you need to delete the associated table. If you need to delete the associated table, you should

has_many :chatrooms , dependent: :destroy

However, if you do not want to delete the associated table, you should

has_many :chatrooms , dependent: :nullify

There are detailed descriptions in the rails API

:dependent
  Controls what happens to the associated objects when their owner is 
  destroyed. Note that these are implemented as callbacks, and Rails 
  executes callbacks in order. Therefore, other similar callbacks may 
  affect the :dependent behavior, and the :dependent behavior may 
  affect other callbacks.

    :destroy causes all the associated objects to also be destroyed.

    :delete_all causes all the associated objects to be deleted directly 
    from the database (so callbacks will not be executed).

    :nullify causes the foreign keys to be set to NULL. Callbacks are not executed.

    :restrict_with_exception causes an exception to be raised if there are any associated records.

    :restrict_with_error causes an error to be added to the owner if there are any associated objects.

    If using with the :through option, the association on the join model must be a belongs_to, and the records which get deleted are the join records, rather than the associated records.

    If using dependent: :destroy on a scoped association, only the scoped objects are destroyed. For example, if a Post model defines has_many 
    :comments, -> { where published: true }, dependent: :destroy and destroy is called on a post, only published comments are destroyed. 
    This means that any unpublished comments in the database would still contain a foreign key pointing to the now deleted post.

RailsApi

like image 1
YaEvan Avatar answered Nov 19 '22 14:11

YaEvan