Cascading and Deleting Model Relationships
In this lesson, we'll learn how to account for foreign key constraints when deleting relationships using our Lucid Models. We'll then learn how we can automatically cascade deletions through to relationships.
- Author
- Tom Gobich
- Published
- Mar 22
- Duration
- 5m 16s
Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
Cascading and Deleting Model Relationships
-
(upbeat music)
-
In our basics of CredList,
-
we learned how we can delete records using a model instance,
-
but this has changed a little bit
-
since we've now introduced a relationship
-
into our user and role models,
-
and we have data relying on that foreign key constraint.
-
So let's dive into a Node.Ace REPL session
-
and let's await load models.
-
And let's get an instance of our user
-
since our user roles are the ones
-
that have current relationship data
-
bound by a foreign key.
-
So we'll await models user,
-
find or fail our first user.
-
Let's just do user.serialize
-
to ensure that we've gotten back a user.
-
And there is Dolores Bosco.
-
Okay, cool.
-
Let's also await user load profile
-
to verify that this user does have a profile.
-
So we should be able to do user profile serialize now
-
to see their profile information.
-
And there we go.
-
So at this point,
-
our user's profile is dependent upon the existence
-
of our user's record with an ID of one.
-
And there's a foreign key constraint
-
between our profile and our user records,
-
ensuring that any user ID that we have here
-
must exist inside of our actual users table.
-
So if we were to attempt to just await user delete
-
to delete our user out of the database,
-
we're actually gonna get an error
-
due to a violation of that foreign key constraint.
-
And you can see right here on the detail section,
-
key ID of one is still referenced from the table profiles.
-
So in order for us to delete our user with an ID of one,
-
we need to delete the profile
-
dependent on that user ID of one as well.
-
And that's true for any relationship
-
that we have bound by a foreign key constraint.
-
So if we were to await user profile delete,
-
this will delete our profile out of the database
-
for a user with an ID of one.
-
And that will then allow us to do user.delete
-
to delete our user with an ID of one.
-
Now that's the manual approach
-
to foreign key constraint relationship deletions.
-
The alternative approach is to have that on delete cascade
-
automatically for us,
-
so that whenever we actually delete out our user
-
with an ID of one,
-
their profile that's dependent on that user record
-
that we're deleting will be deleted out as well.
-
So let's jump into our code base
-
and let's take a look at that approach.
-
So if we dive into our profile migration,
-
because this is the migration where that user ID
-
and his foreign key are being defined,
-
we can chain onto the end of this columns declaration
-
and on delete call and specify the command for this
-
to be a cascade.
-
This will inform our database
-
that anytime that we delete the user with an ID of one,
-
we're also going to want to delete this profile record
-
that's dependent on that user with an ID of one as well.
-
So let's give that a save.
-
Let's hide our text editor back away.
-
Let's exit out of a REPL session, clear that out.
-
Node, ACE, migration, refresh, hyphen, hyphen, seed
-
to roll back, re-migrate and re-seed everything.
-
We'll clear that out
-
and let's open our REPL session once more.
-
Well, await, load models.
-
Let's grab our instance of our user again.
-
Wait, models.user.findOrFailUserWithAnIDOfOne.
-
Verify that we have a user here.
-
Cool.
-
And then we'll hit the button, await user load profile.
-
Let's verify that they have a profile here as well.
-
There we go.
-
Okay, so here we've just verified
-
that we have a foreign key constraint
-
between our profile and our user records.
-
So at this point, if we were to try and await user delete
-
and we run that, we'd expect it to succeed.
-
And there it did.
-
If we check out our user model,
-
we'll see that isDeleted is set to true.
-
And the reason why that succeeded this time
-
is because we have that on delete cascade
-
informing our database that we want to cascade
-
the deletion of a user into their profile as well.
-
Now, one thing to note here is that
-
if we take a look at our user profile,
-
since we still have everything in memory,
-
we're gonna see that the isDeleted flag is set to false.
-
The reason for this is because the cascade behavior
-
is at the database level, not at the model level.
-
So at this point, the profile has actually been deleted,
-
but our model instance is just out of date.
-
So the behavior that we're seeing here
-
is expected and correct.
-
We just didn't delete through this profile,
-
so it doesn't actually know that it's deleted.
-
So let's go ahead and try and refresh our profile.
-
So we could do await userProfile.refresh
-
to try and grab a fresh instance out of our database.
-
If we run that, we're gonna see that we get back
-
model.refreshFailed because it was unable
-
to look up a profile with an ID of one,
-
meaning essentially that it has been deleted
-
and no longer exists in the database.
-
We can confirm that by doing await modelsProfile
-
findByOrFail, we'll find by the user ID column
-
with an ID of one.
-
If we run that, we're gonna get an E, we're not found
-
exception because it could not find a profile
-
with a user ID of one, furthermore confirming
-
that the record has been deleted.
-
Before we round out this lesson,
-
let's go ahead and apply this on delete cascade
-
in a couple more places.
-
So where this would be handy is between our cast movies
-
and crew movies pivot tables.
-
Nope, that's our altered one.
-
Let's take a look at the actual one
-
that's defining the column for the relationship here.
-
So if we delete a cynist or a movie out of the database,
-
we're no longer going to want our pivot table existence
-
to be here because if the cynist no longer exists
-
and the role in that movie no longer exists
-
and the vice versa, if the movie no longer exists
-
then the cynist role no longer exists either.
-
So it would make sense to go ahead and just cascade
-
that deletion for both of these here.
-
So add that in for both of those,
-
and we'll jump over to our crew movies
-
and apply it here as well for both of them, just like so.
-
Let's go ahead and refresh our database
-
so that we have everything up to date.
-
Exit out of a REPL session, clear that out,
-
node, base, migration, refresh, hyphen, hyphen, seed.
-
Give that a run, and there we go.
-
Introduction
-
Fundamentals
-
2.0Routes and How To Create Them5m 23s
-
2.1Rendering a View for a Route6m 29s
-
2.2Linking Between Routes7m 51s
-
2.3Loading A Movie Using Route Parameters9m 17s
-
2.4Validating Route Parameters6m 6s
-
2.5Vite and Our Assets6m 38s
-
2.6Setting Up Tailwind CSS9m 5s
-
2.7Reading and Supporting Markdown Content4m 32s
-
2.8Listing Movies from their Markdown Files8m 51s
-
2.9Extracting Reusable Code with Services7m 4s
-
2.10Cleaning Up Routes with Controllers4m 52s
-
2.11Defining A Structure for our Movie using Models9m 38s
-
2.12Singleton Services and the Idea of Caching6m 11s
-
2.13Environment Variables and their Validation4m 16s
-
2.14Improved Caching with Redis10m 44s
-
2.15Deleting Items and Flushing our Redis Cache6m 46s
-
2.16Quick Start Apps with Custom Starter Kits6m 28s
-
2.17Easy Imports with NodeJS Subpath Imports8m 40s
-
-
Building Views with EdgeJS
-
3.0EdgeJS Templating Basics8m 49s
-
3.1HTML Attribute and Class Utilities6m 9s
-
3.2Making A Reusable Movie Card Component10m 24s
-
3.3Component Tags, State, and Props4m 53s
-
3.4Use Slots To Make A Button Component6m 56s
-
3.5Extracting A Layout Component5m 13s
-
3.6State vs Share Data Flow2m 59s
-
3.7Share vs Global Data Flow6m 7s
-
3.8Form Basics and CSRF Protection6m 13s
-
3.9HTTP Method Spoofing HTML Forms3m 3s
-
3.10Easy SVG Icons with Edge Iconify7m 57s
-
-
Database and Lucid ORM Basics
-
4.0Configuring Lucid and our Database Connection4m 3s
-
4.1Understanding our Database Schema9m 35s
-
4.2Introducing and Defining Database Migrations18m 35s
-
4.3The Flow of Migrations8m 28s
-
4.4Introducing Lucid Models5m 43s
-
4.5Defining Our Models6m 49s
-
4.6The Basics of CRUD11m 56s
-
4.7Defining Required Data with Seeders11m 11s
-
4.8Stubbing Fake Data with Model Factories13m 48s
-
4.9Querying Our Movies with the Query Builder15m 30s
-
4.10Unmapped and Computed Model Properties3m 24s
-
4.11Altering Tables with Migrations7m 6s
-
4.12Adding A Profile Model, Migration, Factory, and Controller2m 57s
-
4.13SQL Parameters and Injection Protection9m 19s
-
4.14Reusable Query Statements with Model Query Scopes8m 11s
-
4.15Tapping into Model Factory States9m 15s
-
4.16Querying Recently Released and Coming Soon Movies4m 59s
-
4.17Generating A Unique Movie Slug With Model Hooks7m 59s
-
-
Lucid ORM Relationships
-
5.0Defining One to One Relationships Within Lucid Models5m 49s
-
5.1Model Factory Relationships2m 54s
-
5.2Querying Relationships and Eager Vs Lazy Loading5m 17s
-
5.3Cascading and Deleting Model Relationships5m 16s
-
5.4Defining One to Many Relationships with Lucid Models6m 56s
-
5.5Seeding Movies with One to Many Model Factory Relationships5m 24s
-
5.6Listing A Director's Movies with Relationship Existence Queries8m 41s
-
5.7Listing and Counting a Writer's Movies8m 41s
-
5.8Using Eager and Lazy Loading to Load A Movie's Writer and Director5m 18s
-
5.9Defining Many-To-Many Relationships and Pivot Columns9m 48s
-
5.10Many-To-Many Model Factory Relationships4m 50s
-
5.11A Deep Dive Into Relationship CRUD with Models18m 5s
-
5.12How To Create Factory Relationships from a Pool of Data13m 55s
-
5.13How To Query, Sort, and Filter by Pivot Table Data9m 47s
-
-
Working With Forms
-
6.0Accepting Form Data12m 15s
-
6.1Validating Form Data with VineJS9m 29s
-
6.2Displaying Validation Errors and Validating from our Request7m 16s
-
6.3Reusing Old Form Values After A Validation Error2m 3s
-
6.4Creating An EdgeJS Form Input Component5m 28s
-
6.5Creating A Login Form and Validator5m 1s
-
6.6How To Create A Custom VineJS Validation Rule9m 7s
-
-
Authentication & Middleware
-
7.0The Flow of Middleware7m 49s
-
7.1Authenticating A Newly Registered User4m 14s
-
7.2Checking For and Populating an Authenticated User2m 10s
-
7.3Logging Out An Authenticated User2m 24s
-
7.4Logging In An Existing User6m 54s
-
7.5Remembering A User's Authenticated Session6m 55s
-
7.6Protecting Routes with Auth, Guest, and Admin Middleware5m 36s
-
-
Filtering and Paginating Queries
-
8.0Creating A Movie List Page3m 43s
-
8.1Filtering A Query By Pattern Likeness7m 9s
-
8.2Filtering Our List by Movie Status5m 47s
-
8.3How To Apply A Dynamic Sort Filter To Your Query7m 12s
-
8.4Joining SQL Tables To Order By A Related Column4m 49s
-
Validating Query String Filter Values7m 23s
-
How To Paginate Filtered Query Results9m 15s
-
Pagination First, Last, Next, and Previous Buttons4m 2s
-
-
User Watchlist
-
An Alternative Approach to Many-To-Many Relationships4m 56s
-
Toggling A Movie in an Authenticated User's Watchlist9m 56s
-
Listing and Filtering User Watchlist Items7m 34s
-
Allowing Users To Toggle A Movie As Watched4m 44s
-
Filtering By User's Watched Status6m 7s
-
Defining A Composite Unique Constraint4m 46s
-
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!