Removing an Item
00:00 In the last lesson, you worked on the feature to update a to-do item, setting its done status to true. marking the item as completed. In this lesson, you work on the feature to remove a to-do item from the database.
00:15
And again, the place to get started is your todoer class to include the functionality to remove a to-do item. With that, you define a new method, call it remove()
, and this method would require a todo_id.
00:28
You can type annotate this to specify that this should be an int and the return value will be an instance of CurrentTodo
. First, the good practice is a doc string or documentation for this method.
00:45 And the general approach here is the same as you had earlier. You read all the to-do items from the database, you identify the to-do item you want to remove, and then you run the remove operation.
00:55
And finally, write back the new updated list to the database. And to do this, you start by reading all the to-do items. And for that, use the db_handler
colon, the read_todos()
method, and then you can assign the value or the result to a variable called read
.
01:12
The first thing to check is if the operation was successful, and for that, you check if there’s an error that is nonzero. If so, return an instance of CurrentTodo
01:23 but pass an empty dictionary. And if the operation is successful, it’s time to identify and remove that to-do item from the list. You can use a try and except block.
01:36
An expected error could be an IndexError
for when users pass an ID in a list that doesn’t exist. And if that happens, you can return also an instance of CurrentTodo
, an empty dictionary object, and an ID_ERROR.
01:52
And in the main try
block, you identify and remove the to-do item. First, get into the list of to-dos and then using Python lists’ .pop()
method, pass in the ID.
02:03 But also, you know Python is zero-indexed. So to identify the to-do item of interest, you’d have to use to-do ID passed minus one. With that to-do item removed from the list, you then write back the new updated list to the database.
02:19
You’ll also use the db_handler
call the write_todos()
method,
02:24
passing it the updated to-do list. And you can assign the response of this operation to a variable called write
to get the response code.
02:34
And with that, finally return an instance of current to-do with the to-do item you deleted and the output error of the write operation. Now the next thing to do is to implement the remove typer
CLI command.
02:48
And for that, you head to the cli.py
module.
02:53
And right under the set_done
command, you’d create a new CLI command calling it def remove()
. And as you know, this is just a regular function until it’s made a typer
CLI command using the @app.command()
decorator.
03:07
You can type annotate this function to specify that the return value will be None
and add a docstring.
03:14
Now to address the parameters needed for this command, we take just a todo_id
and using typing extensions, Annotated
03:24
to give more details about this parameter. It’s of data type int
and it is going to be a type of typer.Argument()
and using the ellipses, specify to typer
that this argument is absolutely required.
03:36
Then you add the help text. You save this, you can head over to your terminal to confirm that this command has been registered by running python3 -m
rptodo --help
.
03:50
And you should now see the new remove
command added to your CLI application.
03:56
Coming back to the parameters, you’d actually include another parameter called force
. This is to be Boolean flag that specifies whether to force delete an item or prompt the user to confirm the deletion operation before deleting.
04:12 So with the force flag passed, the remove command will remove the to-do item without asking for confirmation. But if not passed, then the user can be prompted to confirm that they indeed want to delete the item.
04:24
And so also using Annotated
from typing extension, you can specify a few things. First is that this is going to be Boolean type and that it’s going to be the type of typer.Option()
.
04:36
Then you specify the flags to receive this value, one of which would be --force
and another would be -f
.
04:46
And then you add the is_eager=True
to specify to typer
that this option takes precedence in this command. Finally, some help text.
04:59
As this is a Typer option, you can set a default value. You can have this as False
to specify that the force flag is not active by default. But for the sake of this project, I’ll set mine as True
to ensure that the force flag is always applied by default.
05:14
You can play around with this to see the difference. You’d see where this is used down the line, and as you had before, the first thing to do is to get an instance of the todoer class to a variable called todoer
.
05:25
You’ll assign the value of calling the function get_todoer()
and this should return an instance of a todoer class. Because this class will hold the method to run the actual removal. First, you can create a helper function that will run the actual to-do removal.
05:40
And that helper function you can call _remove()
. The naming convention starting with an underscore signifies that this is an internal function.
05:49
And here you can define the actual removal operation. And for that, using the to-do instance, you call the remove()
method, and pass it the todo_id
.
05:59
You do assign this to a variable that holds the value for the to-do and the response code. Then you check if any error occurred by checking if error
and then you could let the user know using typer.secho()
.
06:15 Into this, you pass along the message “Removing to-do with ID the passed to-do ID failed with.” Then you try to get the error message just to ensure that f-string doesn’t error out because adding another double quote within, you can put the outer quotes as single quotes.
06:36 Then using the errors dictionary, you can obtain the actual error in a human-readable format,
06:43
then style accordingly. And if this happens at this point, you can exit the operation by raising typer.Exit()
, the exit code of one. But all things go as planned, you can let the user know that the to-do item has been removed.
06:59
That is in the else block. Also using typer.secho()
07:05 docstrings, you can say “To-do with ID the passed todo_id for more details.” You can get the description, current brackets access the to-do and get the description value was removed.
07:19 With this helper function now in place, the next thing to do is to check if the directly force remove a to-do item or ask for confirmation. This is where setting the value of force comes into play.
07:31 If force value was set to false, the user is asked for a confirmation before the to-do item is removed. But if not, the to-do item is directly removed. For that, you can say if force is true, call the remove helper function, but if not, in the else block, you ask for a confirmation from the user. To do that, first, get a list of all the to-dos to get more detail about that, to-do item to be deleted.
07:57
And for that, using the todoer class, you call the get_todo_list()
method
08:02 and this should return the list of all the to-dos in the database. And now you try to identify the to-do item to be deleted. And for that, you can use a try and except block.
08:12
A possible expected error here will be an IndexError
where you try to access an item from a list with an index that doesn’t exist. And if that happens, you can let the user know that no such to-do item exists. For that, you also use typer.secho()
“Invalid to-do ID” and style that accordingly.
08:32
And if that happens, you can raise and exit the operation at this point using raise typer.Exit()
with exit code one.
08:42
Now with the main try block, try to access the to-do item with a given to-do ID into a variable called todo
using Python list indexing. Try to access the to-do item with the given to-do ID.
08:53 But as Python is zero-indexed, you know the position of the actual to-do item of interest would be that in index position minus one. If the operation in the try block succeeds, that is, you get the to-do item you need, you go ahead to try to delete that to-do item.
09:10
But since the force flag is false, you’d ask the user for a confirmation. And to that, you recall the response in a variable called delete
and use typer.confirm()
.
09:21 You can now confirm the operation from the user with an f-string you can say “Delete to-do with ID the given to-do ID and description.” Now that you have the to-do item, you can access its description.
09:35
So this is a confirmation to the user. And if the user confirms this, the value of this delete variable will be true and you can check if delete, then call the helper function _remove()
.
09:48
But if not, you can let the user know that the operation has been canceled using typer.echo()
, the simple error code time as no fancy style is needed.
09:58 And to that, you pass “Operation cancelled.”
10:03 You’ve just implemented the to-do item removal feature by creating the remove typer CLI command. You can take this for a spin by heading over to your terminal.
10:13
You can list and view the to-do items you have in your database. python3 -m rptodo list
10:22 Running this, I get a little error, which is as a result of a mismatch in my Python f-string formatting. So this accessing the description value from the to-do item should be in a single quote, not a double quote.
10:35 And also there should be a second one down below. Also correcting this one.
10:43 And this should fix the problem. I save
10:47
and try to list my to-do items again. I now see my to-do items listed in my terminal. I’ll try to remove a to-do item from this list using python3 -m rptodo remove
command and passing it an ID one.
11:05 And I get the message “To-do with ID one, Get some milk, was removed.” This is because the force flag in my version is set to True. If you had yours as False, you first get a confirmation before the delete operation.
11:20 With that, you’ve just completed the feature to delete or remove a to-do item from the to-do database. In the next lesson, you’ll have an overview of what we’ve built so far.
Become a Member to join the conversation.