Giter VIP home page Giter VIP logo

phase-4-deleting-associated-data's Introduction

Deleting Associated Data

Learning Goals

  • Understand dependencies between models based on their relationships
  • Delete child records when the associated parent record is removed

Introduction

In this lesson, we'll be adding delete functionality to our API so that users can remove a dog house from the database. We'll keep using the same starter code from the previous lesson. You can set up the models with:

$ bundle install
$ rails db:migrate db:seed

Deleting Associations

In our AirBudNB app, there is a one-to-many relationship between a dog house and its reviews:

AirBudNB entity relationship diagram

Recall that at the level of the database, this means that for every entry in the reviews table, there is a dog_house_id foreign key column that points to the row in the dog_houses table:

AirBudNB reviews table

Imagine we're creating a feature to give users the ability to delete a dog house from the site. When that dog house is deleted, what should happen to the reviews? Both from our users' perspective and from the database's perspective, it doesn't make much sense to keep a review around if there's no dog house for it to be associated with.

In fact, if you try removing a record from the database now, you'll see an error! Try this out in a Rails console session with rails c:

DogHouse.first.destroy
# => ActiveRecord::InvalidForeignKey (SQLite3::ConstraintException: FOREIGN KEY constraint failed)

The issue is that this dog house has reviews associated with it:

DogHouse.first.reviews
# => #ActiveRecord::Associations::CollectionProxy [#Review id: 1, ...

Those reviews must have a valid dog_house_id for their foreign key because of a database constraint that was established when we created the reviews table:

class CreateReviews < ActiveRecord::Migration[6.1]
  def change
    create_table :reviews do |t|
      t.string :username
      t.string :comment
      t.integer :rating

      # foreign_key: true establishes a relationship between a review and a dog house
      t.belongs_to :dog_house, null: false, foreign_key: true

      t.timestamps
    end
  end
end

So before removing the dog house, we must first remove the reviews. We can do this manually from the Rails console:

DogHouse.first.reviews.destroy_all

Notice in the SQL generated by Active Record, this finds all the associated reviews for the dog house and deletes them from the database:

  DogHouse Load (0.3ms)  SELECT "dog_houses".* FROM "dog_houses" ORDER BY "dog_houses"."id" ASC LIMIT ?  [["LIMIT", 1]]
  Review Load (0.2ms)  SELECT "reviews".* FROM "reviews" WHERE "reviews"."dog_house_id" = ?  [["dog_house_id", 1]]
  Review Destroy (0.4ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 1]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 2]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 3]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 4]]

After deleting the reviews, we can then safely delete the dog house:

DogHouse.first.destroy

However, there is a better way!

Using dependent: :destroy

As part of the class definition for our DogHouse model, we included the has_many association reference:

# app/models/dog_house.rb
class DogHouse < ApplicationRecord
  has_many :reviews
end

This is what lets us easily find all the reviews associated with a dog house instance by simply calling .reviews on any instance of the DogHouse class.

The has_many association reference also lets you provide additional options to customize its behavior. In our case (and in many cases involving a one-to-many relationship), we can use the dependent: :destroy option. This will tell Active Record to delete all the associated records when the parent record is deleted.

Exit the Rails console session, then add this code to the DogHouse class:

# app/models/dog_house.rb
class DogHouse < ApplicationRecord
  has_many :reviews, dependent: :destroy
end

So as soon as we call .destroy on an instance of a DogHouse, all the reviews associated with that instance will be destroyed! Restart the Rails console and try it out:

DogHouse.second.destroy

No more error! We were able to delete the dog house from the database along with its associated reviews with just this one line of code. And the SQL generated by Active Record matches our two-step approach from earlier of deleting the reviews first, then deleting the dog house:

  DogHouse Load (0.1ms)  SELECT "dog_houses".* FROM "dog_houses" ORDER BY "dog_houses"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
  TRANSACTION (0.1ms)  begin transaction
  Review Load (0.1ms)  SELECT "reviews".* FROM "reviews" WHERE "reviews"."dog_house_id" = ?  [["dog_house_id", 2]]
  Review Destroy (0.4ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 5]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 6]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 7]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 8]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 9]]
  Review Destroy (0.1ms)  DELETE FROM "reviews" WHERE "reviews"."id" = ?  [["id", 10]]
  DogHouse Destroy (0.1ms)  DELETE FROM "dog_houses" WHERE "dog_houses"."id" = ?  [["id", 2]]

Conclusion

It's always a good idea to clean up any unused data in the database when deleting records, and to make sure there aren't any records that lose a necessary association when their parent record is deleted. With Active Record, we can use the dependent: :destroy option to automatically remove associated records when the parent record is deleted.

Resources

phase-4-deleting-associated-data's People

Contributors

ihollander avatar lizbur10 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.