Python FastAPI Tutorial (Part 6): Completing CRUD - Update and Delete (PUT, PATCH, DELETE)
36:22

Python FastAPI Tutorial (Part 6): Completing CRUD - Update and Delete (PUT, PATCH, DELETE)

Corey Schafer 15.01.2026 7 688 просмотров 439 лайков

Machine-readable: Markdown · JSON API · Site index

Поделиться Telegram VK Бот
Транскрипт Скачать .md
Анализ с AI
Описание видео
In this video, we'll be learning how to complete our CRUD operations in FastAPI by implementing PUT, PATCH, and DELETE endpoints. We'll cover the difference between PUT requests for full updates and PATCH requests for partial updates, add delete functionality for both posts and users, and configure cascade deletion so that when a user is deleted, all of their posts are automatically removed as well. By the end of this tutorial, you'll have a fully functional API where you can Create, Read, Update, and Delete resources with proper validation and error handling. Let's get started... The code from this video can be found here: https://github.com/CoreyMSchafer/FastAPI-06-Complete-Crud Full FastAPI Course: https://www.youtube.com/playlist?list=PL-osiE80TeTsak-c-QsVeg0YYG_0TeyXI Pydantic Tutorial - https://youtu.be/M81pfi64eeM ✅ Support My Channel Through Patreon: https://www.patreon.com/coreyms ✅ Become a Channel Member: https://www.youtube.com/channel/UCCezIgC97PvUuR4_gbFUs5g/join ✅ One-Time Contribution Through PayPal: https://goo.gl/649HFY ✅ Cryptocurrency Donations: Bitcoin Wallet - 3MPH8oY2EAgbLVy7RBMinwcBntggi7qeG3 Ethereum Wallet - 0x151649418616068fB46C3598083817101d3bCD33 Litecoin Wallet - MPvEBY5fxGkmPQgocfJbxP6EmTo5UUXMot ✅ Corey's Public Amazon Wishlist http://a.co/inIyro1 ✅ Equipment I Use and Books I Recommend: https://www.amazon.com/shop/coreyschafer ▶️ You Can Find Me On: My Website - http://coreyms.com/ My Second Channel - https://www.youtube.com/c/coreymschafer Facebook - https://www.facebook.com/CoreyMSchafer Twitter - https://twitter.com/CoreyMSchafer Instagram - https://www.instagram.com/coreymschafer/ #Python #FastAPI

Оглавление (8 сегментов)

Segment 1 (00:00 - 05:00)

Hey there. How's it going everybody? In this video, we're going to be learning how to complete our CRUD operations in fast API. So the last tutorial we set up a database with SQL Alchemy and we implemented the create and read parts of CRUD. We created a user model and a post model with a relationship between those. Uh we can create users and post with a post request. We can read user and post information with getit request. And since we set up that relationship, our post responses include the full author information automatically and our users contain information about which post they've written. So that's really nice how it's currently set up. But now we're going to complete those CRUD operations by adding the update and delete functionality. So by the end of this video, we'll have a full API where we can create, read, update, and delete both users and post. And then we'll also configure cascade delete so that if we delete a user then their posts get deleted along with them. And throughout all this we'll test out everything inside of the interactive docs and uh take a look at our front-end view for the API as well. So let's go ahead and get started. All right. So first let's talk about updating our information. So when you're building REST APIs and you want to update something, you generally have two different update methods available and those are put and patch. So put is a full replacement. So with put, you send all of the fields for that resource. You can think of it as replacing the record with a new version of that record. Patch is for partial updates. So with patch, you only send what has changed. And if you only want to update a title for a post, then you just send the title and everything that you didn't send stays the same. Now, in practice, I'd say that patch is usually more useful for APIs because you don't want to force clients to send the entire resource when they're only changing, you know, one or two fields. But we're going to implement both here so that you can see what each looks like and when you use each. So the first thing we need is a schema for partial updates to post because with patch we want to allow clients to send only the fields that they want to update. And right now we have a post base, a postcreate and a post response. For a patch request we want a schema where all the fields are optional because we might only update a title or only update the content. So right after postcreate here I'm going to add a new schema called postcreate and I'm going to use this post base as a starting point here and we will do a post update here. I'll call this post update. So now I'm going to make these optional here. So I'm going to say that this can be string or none. I'll do the same thing for content there. And now since they are optional, we have to have a default here. So I'm going to set a default to none here. And I will also copy this for our content with a default of none as well. And we also still have our field validations in here. So if a value is provided, then it still has to pass the validation. So if they still uh send a title, it still has to be at least one character and at most 100 characters. Now, one important thing here is that I'm not including the user ID in post update. Typically, you wouldn't want to allow changing ownership through a partial update endpoint. If you really needed to reassign a post, then you'd probably do that with a put request or you might even have a dedicated endpoint for transferring ownership like that. Okay, so I'm going to save our schemas here. Now, let's go back to main. py and now let's import that schema as well. So I'm going to include that. So I will import post update here and save that. All right. So now let's implement our update endpoints for post. Now we already have a get post endpoint. So let me find that. And I want the one that gets a single post here. Okay. So I'm going to add our update endpoints right after this. Uh it only makes sense. uh right after this we have a get post and update post we'll put right after this. Now I'm going to copy this as a starting point here. So I will just copy and paste this entire thing. And now let's go ahead and change some things here. So first of all let's make this a put method. So instead of get we have put and instead of get post here I will call this update post. Now remember we have partial and full. Put is for uh full. So I'll do a update post full here. Now for a full update we want all of the fields to be required.

Segment 2 (05:00 - 10:00)

So instead of post update that we like we just created we're going to reuse postcreate because postcreate already requires title content and user ID. So I'll add the post data parameter into our function signature here. I'll do that right after post ID. So I'll say that the post data and this will be post create and let me put a comma here just to clean up the formatting a bit. So this is what will contain the data from the request body when someone is attempting to update a post. Okay. So now we need to flip this conditional logic here. Right now, we return the post if it exists. But for an update, we want to check if the post doesn't exist and raise a 404 right away because we don't want it to try to update a post that doesn't exist. So, let me instead say if not post and then we will move this HTTP exception into here. Okay. And now we need to add the update logic. But before we update the user ID, we should check if the new user actually exists. So this gives us a nicer error than letting the database constraint fail. Uh we only do this check if the client sent a different user than what is already on the post. So I'll add this check here after the 404. So I'll say if that post data user id if that is not equal to the post dot user id then we want to check that the user that they're trying to change this to actually exists. Now let me go ahead I already have a user check right here. So let's go ahead and just grab that from creating the post and I will reuse that logic here. Let me indent that correctly. But instead of the post do user ID here, uh we want this to be the post data user ID. Okay. And once we have authentication set up, we're only going to allow users who created posts to be able to update or delete them. But we're setting it up this way for now. You'll also notice we're reusing a good bit of logic for checking if users or post exists in these routes. That's probably a good indication that we should pull this out into a helper function, but we're not going to do that yet. We will keep it how it is for now. So, continuing on here, finally, if the post exists and the user exists, those are the two checks that we have now. Then, let's update all of the fields on the post. So, outside of the if conditional here, we can just say post. title, title we will set that equal to the post data. title. So I will do that and remember with a put request we have to do all of these. So let me go ahead and copy this and we will do the same thing for both the content and the user ID. So after we've set that information on the post then we can just commit that to the database. So I will do a database. comit here. And now let's also do a database. refresh and we will refresh that post. And then let's just return that post. So let me see if I can fit this entire endpoint in here. I can't fit the entire thing on the screen. Uh but we can see most of this here. I'll scroll up to where we could at least see the function signature here. All right. So now let's test this put endpoint that we just put into place. So first let me run the development server. And now let's go back to our page here and run our documentation. And I will uh refresh this. We can see that we have a route here called update post full. Let me click on that. And now I can update a post here. Uh first let me find one uh that we want to update. So I think that post one, this is just one that says first database post. This is from the last video. Let's update that. So I will go down here to this update post here. Try it out. We will update that first post. And now I have to uh fill in all of these. So for the title here, I will say full updated post all new content. And for the user ID, let's set that equal to one. Uh that is one of my existing users here. So I will ex uh execute that and we can see that now we got a 200 response here and it updated that post. We have a new title, new

Segment 3 (10:00 - 15:00)

content and the user here was an existing user. Now let me see if we can also uh change to a different user here. If I look at our users. So if I go to get users here, then I also have a user 2 that we created in the last tutorial called Jane Doe here. And she has an ID of two. So let me go back to our uh post update here. Let me change this to A2. We will execute that. And now Jane Doe is the owner of that updated post. Okay. So let me reload the documentation here. And now let's go back to our code. And now let's implement the patch endpoint for a partial update. Now for patch we're going to use post update because that schema has optional fields. So I'm going to use uh the put route that we just created here as a starting point. So let me copy all of this. And then right under here is where I'll put the partial update. So let me go up here. This is going to be patch method. And let's call this function update post partial instead since this is going to be a partial update. Now instead of postcreate here, I want this to be post update because these are going to be optional fields. And since post update doesn't include user ID, we can remove that entire uh user block validation there. So I will just go ahead and remove that. And let's also these direct field assignments because we'll be replacing these with a more dynamic approach. And now here's the key to making patch work correctly. We only want to update fields that the client actually sent. We don't want to overwrite missing fields with none. So I'm going to say that update data is equal to post data and we are going to do a model dump here. And with model dump I'm going to set exclude unset equal to true. Now remember this post data right here. uh this is what will contain the data from the request body when someone is attempting to update a post. So that's what that post data is there. Now let me explain more about what exclude unset equal to true does here because this is the thing that makes patch work correctly. So if a client was to send you know something like title is equal to new title and that's all they sent they didn't send any other fields then without exclude unset equal to true then paidantic would include all fields with their defaults. So, if our content had a default of none, then that would be included as none in the update, which obviously isn't what we would want because that would set our new title, but then our content would get wiped out. But with exclude unset equal to true, we only get what the client actually sent. Okay, so that model dump is going to give us a dictionary. And now we are just going to loop over that dictionary and use set adder to dynamically set each provided field on the post object. So what I can say here is we can just say for the field and value in update data do items. And what we want to do here is we want to use set adder and we want to set the attribute on a post and we want that to be the field and the value. So let me explain exactly what's going on here now. So let's say that post data has a new title and so the title will be the field and the value let's just say that it's going to be new title is the value. So then we have a dictionary that just has a key of title and a value of new title. So we loop over that and now we're saying for post we want to set the attribute of field which is going to be title and we want to set that equal to value which is going to be new title. So that just dynamically sets each provided field on that post object and that's exactly what we want. Okay. So then we are committing and then we are refreshing and then we return that updated post. Okay. So now let's test the patch endpoint. So I'm going to go back to the docs here and reload this. Now we can see that we have update post partial here. So now let's go to actually let me go to get post here and see what our posts currently look like.

Segment 4 (15:00 - 20:00)

Okay. So the one that we changed before we have that full updated post and we changed the user to Jane Doe. Now let's go ahead and update that. That is a post with an ID of one and the user ID is two. Uh my user ID is one if we want to update that. Okay. So I will go to try it out here. We will update post one. And now let's do partial update. And I'll just go ahead and get rid of the content here. We only want to update the title. So, we'll do partially updated title. And let's execute that. And we can see that we got a 200 response. And we updated the title, but the content stayed the same. Everything else stayed the same. So, this is a lot more flexible than put because clients don't have to send every single field just to change one thing. All right. So now let's add our delete endpoint for post. So I'm going to use the get endpoint as a starting point again here. And that was up here. So I'm going to grab that and I'm going to put this below our partial post update. So right above our exception handlers here. This is where we will add our delete endpoint. So again instead of the get method we want to use the delete method here. Now for delete we usually return a 204 no content response. A 204 means that the request succeeded but there's no response body. So I am going to remove this response model here since there isn't a response and just add a status code instead. So we'll say that the status code is equal to status dot and this is going to be a 204 no content response. And now let's rename this function here to delete post instead of get post. And now I need to flip the conditional like we did before. Uh instead of returning the post if it exists, uh we just want to raise a 404 if it doesn't exist. So I'm going to get that same logic from up here where we changed that. So I will grab that conditional there and let's go down here and put that in. And now instead of returning the post, we will just delete it and then commit. So I'll say db. delete and we will delete that post. And now we can commit that. comit. Okay. And later on when we add authentication, we'll add ownership checks here so that only the author uh of the post will be allowed to delete their post. But for now, we're just going to have it to where anyone can delete any post. Okay. So, I'm going to save this. Let's go back to our documentation here and reload this. Let me uh go back to just the docs here and refresh that. We can see that we now have our delete post here. Now that post that we have been working with has been a post ID of one. So let's just go ahead and delete that one. I'll execute that. We can see that we just get a 204 here saying that it was successful, but there's nothing to return. So that had a post ID of one. So now if we go back to our get post route uh let's go to get a specific post which is this one right here. Now let's look for that post ID of one. If we execute that then we can see we get a 404 not found. So we did delete that. Now we've used a good bit of status codes so far uh throughout this series. So, let me take a quick moment to review these HTTP status codes that we've been using. And I'll pop them up on our screen here as well, just for a reference. And I'll briefly talk over each one of these and what we're using them for so far. So, first we have a 200, which is okay. We're using that for successful get, put, or patch requests. We have 201 created status codes. We're using those for successful post methods for users and posts. Uh we have 204 no content. We are using that for a successful deletion. We have 400 bad request. That is for duplicate usernames or emails when creating users. Uh we have 404 not found. That's when a resource doesn't exist whether that's a user or a post. And we also have 422 unprocessable content or unprocessable entity and that is a validation error and that's automatic from paidantic. So for example that would be whenever we

Segment 5 (20:00 - 25:00)

try to uh submit a user that has an email that is not an email address something like that. And these are all pretty standard REST API status codes. and making sure that we're using these correctly makes our APIs much easier to work with for client applications. All right, so we've completed CRUD operations for post, but we still only have create and read for users. So let's add update and delete for users as well. So we've already seen this process once. Uh but this will kind of solidify it. Hopefully if uh the first time around it didn't make as much sense. So here in our schemas, let's add user update and we will add this after user response. Here I'm going to copy this from our user base. And after user response, I will do a user update. So I'm going to say user update here. And now we want all of these to be optional. So for the username, we can say that is a string or none. We need a default equal to none here. And now let me grab that default there. I will also paste that in for the email. We also want this email to be an email string or none. And now I'm also going to add in image file here. So let me copy the username as a starting point here. And now let's do image file. And we can have this be string or none. uh we will have the field have a default of none, min length of one, max length of let's just do 200 for a file name there. So this is basically the same pattern as we did for post updates. All the fields are optional and default to none and the validations still apply if a value is provided. Now this includes image file which lets users update a user's profile picture file name. Now we're only storing the file name and not the full path. the image uh path property on our model will build the full path that we are actually using in the templates. Okay, so let me save our schemas. py file here. And now let's go back to our main. p py file and let's update this uh our endpoints here or imports to import our user update. So we'll import that. All right. Now let's add our patch endpoint for users. Now, since we have a good sense of what's going on here and have seen me walk through the examples with post updates and deletes, for the sake of time here, I'm going to copy and paste these user routes from my snippets and then we'll go over exactly what we're doing after I put these in place. So, let me first grab these from my snippets here. So, make sure I don't go too far here. Go back to main. py and I'll put this after our users routes here. So, let me go ahead and see which one I want to put this after. Let's put it after uh this one here where we get a user's post. So, I will drop this in here and then we can go over this. Okay. So just like we saw with the post, we have a patch route here and we are patching a specific user ID. Uh this is called update user. Uh we are passing in that user update uh model that we just created. That is what's going to be used to validate the data that people are trying to use in order to update a post. Now the first thing is we find a user and we return a 404 if that user does not exist. That's what we're doing. We are doing here. And then what we're doing here is if the user requests a new username, we check if it's different from the current username. And if it is different, then we do a query to see if that username already exists in the database. If it does, then we return a 400 bad request. And we do the exact same thing for email. So here we are just making sure that they're not changing their username to a username that's already in use or an email that is already in use when they're trying to update that. And then after we validate that those updates are all safe, then we can update only the fields that were provided. Now, I did this a little differently here. I just wanted to show kind of a manual way that you could do this if you didn't want to use that set adder that we used before. Some people have never used that before and might not know how to use it. Uh so if you have um a model that you know just has a few fields like this then there's no harm in just doing a manual check uh like this. So we can say if the user update usernusername is not none. So that basically means if they passed in a

Segment 6 (25:00 - 30:00)

username to update then set that username. If they passed in an email to update then set that email and so on. Then after we set those fields we commit. We refresh and then we return that updated user. Now you'll notice that we're only implementing patch for users. We showed both put and patch for post earlier so that you could see the differences between a full and partial update. But in practice, patch is usually what you want for both. It's rare that you need to completely replace a resource rather than just update a few specific fields. So for user, I'm just going to have create user and a partial update for user here, but I also wanted to show that put method with the post. So now, let me save that. Let's go back to our documentation here and we can reload this. Now let's go down here and see where we can update a user. So we have update user right here. Let me go to try it out. So let's update user one. And I'll just do a partial update here. So for the username, let's do Corey updated because I believe uh I am the first user here. So I'll do query updated for that. Execute that. We can see that we get a 200 response there. Corey updated as the user and everything else stays the same. All right. So, one of the only things left to do here is to implement a delete for users. But before we do that and add a deletion for users, we need to think about something important here. So, when a user is deleted, what happens to a user's post? So, we basically have two options. So we could prevent deletion if the user has post which is the safest approach or we could delete the user and cascade delete all of their post. This is what a lot of real world applications do when you want to delete an account. You know if you've ever gone to delete an account on a service they usually have a big popup that says are you absolutely sure this will delete all of your data. That is a cascade deletion. So, we're going to use a cascade delete to match how a lot of real apps work. Uh, so when we delete a user, it's going to delete their post as well. Now, to set this up, we have to make an update to our database models. So, let me open up my models. py file here and let's look at user. So, right now we have this post relationship here. So we can see that the relationship for a user's post back populates to author here. Now I'm going to change this and I'm going to set this to where we have a cascade and we will set this equal to all delete dash orphan. And what that does, if I save that, what that does is that tells SQL Alchemy that when a user is deleted, it should delete all of their post too. And the delete orphan part also means that if a post is somehow removed from the relationship without being explicitly deleted, then it will be cleaned up. Now, this is a powerful feature, so use it with caution. Uh, in production, you definitely want to warn users before deleting their account. something like, you know, that we talked about earlier like deleting your account will permanently delete all your posts. So, are you absolutely sure? And some things like on AWS and things like that, they actually make you type in like the name of the account that you're trying to delete or something like that. Okay, so now let's add the delete endpoint for users here. I'm going to grab this from my snippets as well. We can see that this is fairly short here. Back in main. py. I'm going to put this under our update for our users. So, right where we were, I will go ahead and add this in here. And now, let's go over this. Now, this is pretty similar to our post delete. So, we are returning a status code of 204 no content. We have a calling this delete user here. We're finding the user from the database. We are returning a 404 if that user doesn't exist. If that user does exist, then we delete them. And then we commit. And the cascade that we set up in models. py will automatically delete all of their post. So no return is needed since we're using this 204 no content up here. So now let's test this. So let me go back to our posts here. And now let me get all posts just to see what we still have out here. So I currently have uh one post by uh me by Corey here and one post

Segment 7 (30:00 - 35:00)

by Jane Doe. Let's add another post by this Jane Doe here. Uh and then we will delete her. So let me create a post here and I will go to try it out another post and another content block and the user ID. Let's do two and execute that. That should be a post by Jane Doe there. And now let's delete that Jane Doe user. So now I will go to I think I need to reload the page here and in order to see our new route. So now I'll go to delete user. Try this out. Let's delete that user with the ID of two. Execute that. We can see that we get a 204 here. And now if I go to uh search for that user. So I will search for that user too. We can see that user is not found. Now here's that cascade delete in uh in action here. If I go to get post, let's execute that. And now we can see that we only have one post here. We only have posts from my account. That Jane Doe's post were completely deleted. All right. So now we've implemented uh full CRUD operations for both users and post. But before we wrap up, let me show you one more thing and that's updating a user's profile picture. So right now all users have the default profile picture and that's in that static folder profile pictures. It's called default. jpeg. But we added an image file to our user update schema. If I look at this, we can see that we have this image file here. So users can have custom profile pictures. So let me demonstrate how this works. So first I need to manually add an image into our media directory. Uh in a real application we'd have a file upload functionality and we're going to add that functionality in a later tutorial. But for now we need to copy this in manually. So I've never tried to move files around here in the VS Code sidebar. It looks like that works. Okay. So, I dragged that Corey. png into this media profile pics there. Um, so now let me close that sidebar. And now, just in case I need to restart the server, then let me do that. I don't think I do, but I might need to. So, now let's go back to our docs here, and I will reload this. And now, let's go ahead and patch a user here. So, a partial update of a user. And now I'll go to try it out for user one. Let's change that image file. Now, that file that I moved in there was called Corey. png. Let's set that. Execute. And we can see that we get a 200 response there. And now the image file is set to Corey. png. And our image path here is now automatically calculating that it needs to be in that media profile pics instead of the static directory. So we're only storing that file name in the database. But the image path property automatically builds that full path. And that was all set up in our models if we remember that. So if I look here, we can see that we're storing the image file and the image path is just this property that is dynamically calculated. But now if I go back to the browser here and look at our homepage, then if I reload this, then you can see that now this is using my profile picture here that I updated instead of the default picture. So, we do actually have the ability to update profile pictures right now, but we just don't have the ability to upload files right now. Uh, we have to manually drag those in there in order to update those locations. But in a future video, we are going to add that functionality to where they can actually upload their own profile pictures uh within the site here. Okay. So, let's quickly recap what we covered here. So, now we have full CRUD functionality for both models. So both resources follow REST principles with proper validation, status codes and error handling. Our database relationships are working nicely together. Uh they automatically include author information in post responses. And we configured that cascade deletion properly so that when a user is deleted, all of their posts go with them. The pattern that we learned here applies to just about any resource that you'd want to add to your API. But I think that's going to do it for this video. Hopefully now you have a good idea of how to implement complete CRUD operations and fast API with proper update and delete

Segment 8 (35:00 - 36:00)

functionality. In the next video, we're going to add async await uh to our application. Right now, all of our database operations are synchronous and converting them to async will give us better performance when handling multiple requests. and we'll talk about when to use async versus sync in our applications and then after that we'll reorganize our code with API routers. So currently our main py file is getting pretty long at this point and we'll break it apart into a more professional uh code organization pattern and structure. But if anyone has any questions about what we covered in this video, then feel free to ask in the comment section below and I'll do my best to answer those. And if you enjoy these tutorials and would like to support them, then there are several ways you can do that. The easiest way is to simply like the video and give it a thumbs up. Also, it's a huge help to share these videos with anyone who you think would find them useful. And if you have the means, you can contribute through Patreon or YouTube. And there are links to those pages in the description section below. Be sure to subscribe for future videos. And thank you all for watching.

Другие видео автора — Corey Schafer

Ctrl+V

Экстракт Знаний в Telegram

Экстракты и дистилляты из лучших YouTube-каналов — сразу после публикации.

Подписаться

Дайджест Экстрактов

Лучшие методички за неделю — каждый понедельник