Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Creating, Updating, and Deleting

00:00 In the previous lesson I showed you how to write an API endpoint that returns a list of books, as well as how to affect the result using query parameters.

00:08 In this lesson, I’ll close out the other parts of CRUD, giving you a complete digital library API.

00:15 The read part of CRUD has been done. So now it’s time for create, update, and delete. You use HTTP POST, PUT, and DELETE for each of those, respectively. In case you’re coding along with curl, know that the default it uses is HTTP GET.

00:32 To use a different method, you use the -X argument, providing the name of the method you want to use. So far you’ve only seen the FastAPI get decorator, but it of course has corresponding decorators for each of the HTTP methods.

00:47 To create or update an object, you’ll need to send book data to the server. The HTTP POST and PUT methods allow you to put content in the HTTP message body.

00:58 In the case of POST, this is exactly how the contents of a form on a webpage gets sent to the server. Fast API usually uses JSON for data, so you’ll need to JSON-encode what you are sending, and you’ll also need to tell the server that the data is JSON by setting the HTTP Content-Type header. In curl, you do that with the -H command-line argument.

01:21 Let’s go add the CUD parts to our CRUD.

01:25 I’m back in books_main.py, and I need to make a couple of changes to our data handling before adding the new functions. When you create a new book, you need to give it an ID, so I’ve got a global counter here containing what the next ID should be.

01:39 When the code starts, it’s set to the current length of the library dictionary.

01:45 You’ve seen how you can return a Pydantic object from a Fast API function. Well, you can also use them to transform and validate the data sent into you inside of create and update calls.

01:57 Remember, HTTP only deals with text, so you’ve got to handle the conversion of types and ensure that the right fields are present, all of which Pydantic does for you.

02:07 Our existing book model includes the ID of the book. When you create a new book, the system has to create that ID, so the client won’t be sending an ID number up.

02:17 Since all the fields are required in our original book, I’ve created a new Book class that is only used during the create step, which has no ID inside of it.

02:27 There are a couple of different ways of approaching this problem. The pattern I’ve used is an in/out pattern, meaning that there is a data-out version of our class as well as a data-in version of our class.

02:39 I honestly don’t really like this approach, as it means you have to maintain two copies of the class. Instead, you could change the Book class so that ID is optional, but then that causes a different problem.

02:51 You have to manually validate the IDs present when it’s required. This is really a pick-your-poison kind of situation. What I try to do when I write code like this is to use inheritance instead.

03:02 You still have two classes, but that way if you add a new field, you only have to do it in one place. I didn’t do it that way here, as I wanted to keep the book code identical, so I went with the easier to read way, even if it results in trickier to maintain code.

03:18 Our create method uses the post decorator. There are a few different schools of thought on what the URL for a POST should be. I’ve gone with the same URL used for listing books.

03:30 Remember, FastAPI maps the URL as well as the HTTP method, so there’s no problem with using the same URL for both the GET and the POST. When you use the post decorator, FastAPI passes the body of the HTTP message to your function as an argument, and by setting the type hint of that argument to NewBook, FastAPI will automatically take the data sent in and run it through Pydantic to validate it.

03:56 If the user did something that doesn’t validate, Fast API takes care of that for you, returning an HTTP error code. This is nice and clean. It means you don’t have to think about all the ways the user can mess up the data.

04:09 As I’m about to create a new book, I need to increment our global ID counter first, and here I’m calling the book’s constructor, setting the id attribute to our new ID, and manually setting the title, author, and pages fields.

04:23 There are fancier ways of doing this, which take less typing if you’ve got a lot of fields, but I’ll save that for the update case. The newly-constructed book then gets added to our digital library dictionary, using the ID as a key,

04:37 and best practice with a create operation is to return the thing that got created, like I’m doing here. Let’s try this out.

04:49 Here, The -X POST argument to curl tells it to use HTTP POST. If you’re not familiar, the backslash at the end of the line is the shell’s continuation character.

05:00 It lets me type more of the same command on the next line.

05:06 The -H argument to curl sets an HTTP header. This is metadata that tells the server info about what you’re sending it. Headers are strings that contain key-value pairs separated by colons.

05:18 Here I’m setting the Content-Type header, which tells the server the kind of data I’m sending it, which in our case is JSON.

05:29 Then finally, the -d argument to curl sets the data payload. Remember, this is a stringified version of a JSON dictionary. The result below is pretty much what I just sent it.

05:40 Our new book, Dune, all 896 pages of it, with the ID 3.

05:46 Let’s check that that worked.

05:52 Now you might realize why I was pretty-printing it before. That’s kind of compact, but at the end of the list there, you can see our new book. Okay, that’s create.

06:02 Now to update. This time I’m using the put decorator. Often with update, you’ll see a URL argument for the ID of the thing being updated. Since our book object contains the ID, I went with a bare URL instead. Potato, potato.

06:19 Similar to POST, the HTTP message body of a PUT gets sent in as an argument to the FastAPI function. Since I’m updating an existing object, I’m using the original Book class here, which includes the ID of the thing that needs updating.

06:34 Here, I’m looking up a reference to the thing in the dictionary that I’m going to update, and now I need to replace the fields inside of that. To do that, I’m going to loop through all of the field names and use setattr() to replace them.

06:48 The first argument to setattr() is the object being changed, our original book from the dictionary. The second argument is the name of the attribute to change, which will be one of the attribute values from the loop, and then the third argument to setattr() is the new value for that attribute.

07:05 In this case, I’m nesting a call to getattr(), retrieving the same field from the update object that was sent in the PUT. I could have just done three attribute assignments, similar to my hard-coded approach in the create version, but I promised you something fancier, so here it is. For three fields, this is overkill, but if you’ve got 50 fields, it’s helpful.

07:26 There are also ways of querying the fields from the objects so that you don’t have to hard-code the list of field names like I’ve done here. Although I did want it a little fancier, I also wanted to keep it simple enough to understand.

07:38 Once the book has been updated, I return it back to the user. There’s a bit of a subtle point here that I just want to mention. Note that the dictionary stores a reference to the book object.

07:49 Since I’m updating the fields on that reference, I don’t have to do anything to the library dictionary. It’s still pointing at the same thing. That’s different from the create case where the item had to be inserted into the dictionary.

08:02 If you’re using a database to store things, doing a PUT means using a different kind of SQL call than the POST. Let’s try this out. -X for PUT,

08:17 same header,

08:27 and there you go. What gets returned is the updated book, changing to the original page count of the hardcover release instead of the paperback one that I had before.

08:36 Let’s verify it, and with query ID 3, I can see that the update happened. One letter left. Let’s delete. This delete action is for a single item, so here I’m using a templatized URL, like when I did, when I fetched a single book. Then all I need to do is delete that from the dictionary, and I’m done.

08:59 I probably should have some error-handling code here in case they send a non-existent ID, but, well, I’m one of those lazy programmers I mentioned earlier. There are two schools of thought about what to return from a delete operation.

09:12 One is to return the deleted object. The advantage of that is the client then can recreate the same object by posting it back. I don’t like school one, as it kind of implies the thing, including its ID, is still in the system. Here, I’ve gone with school two, which is similar to how I handled error messages before.

09:31 This time, though, it’s a success message. Trying it out,

09:40 and verifying,

09:45 and there you go. Scary fish went bye-bye. Somebody must have hit the bullseye on the scuba tank.

09:53 Next up, I’ll show you FastAPI’s built-in Swagger UI.

Become a Member to join the conversation.