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

Storing and Communicating Data

00:00 In the previous lessons, you’ve completed a way to create, initialize, and connect to the to-do database. In this lesson, you dive into the data model. Here you start to think about how to represent and store the to-do data and also build a system or connection to communicate between the CLI and the database.

00:22 First is to understand how you structure a to-do item. A to-do item will contain three main values, a description which will be a text value or series of strings, a priority value, which will be an integer value between one and three, and a done value, which will be a Boolean True for when completed and False for when not completed.

00:46 To communicate with a CLI at any given point in time in a subclass of a named tuple, you use two pieces of data to hold the acquired information of a to-do: first, the to-do itself, and you use a Python dictionary holding the information for the current to-do.

01:02 And second is an error code, the return or error code confirming if the current operation was successful or not. And all of this data is stored in a subclass of a named tuple with appropriately named fields.

01:18 Named tuples are specialized versions of tuples or subclasses of tuples provided by the collections module in Python that enables you to create immutable objects with named attributes.

01:30 With named tuples, you can access its values using descriptive field names and the dot notation.

01:37 Now back in your code editor in your rp_todo package, open the rp_todo.py module. First, you import some required objects from typing.

01:49 You’ll import Any and NamedTuple. This will be used for type notations later. And then from pathlib import Path.

02:01 Next, what you’re going to do is create the subclass of NamedTuple to create an object called CurrentTodo. This will represent the current to-do item in any given operation as you communicate with your CLI.

02:18 You’ve just seen Python subclasses in action. In Python, a subclass is a class that inherits the attributes and methods from another class known as a superclass or parent class.

02:30 With subclasses, you can reuse and extend the functionalities of the superclass, create specialized versions of the existing class, and override class methods to customize certain behaviors.

02:45 And this is Python subclassing in action. And just as discussed, the to-do field will hold a dictionary with keys of type str and values can be of any data type, and then the error field will hold an integer value for the return or error code confirming if a current operation is successful or not.

03:07 The next thing you’ll create is a data container that will allow you to send and receive data from the database. And for this, you’ll use another NamedTuple subclass.

03:18 So go ahead and open your database.py module and from typing import Any and NamedTuple. This will be used for type annotation.

03:29 Next, you define a NamedTuple subclass called DBResponse.

03:37 And you subclass NamedTuple.

03:42 Then define two values: a list of to-dos called todo_list, which will be type annotated as a list of dictionaries with keys that are str and values of data type Any, and then an error with integer values.

04:00 Now this to-do list will represent the list of to-dos you would use to write and hold the values when you read from a database, and the error will be an integer number representing a return code relating to the current database operation.

04:16 Next, you create something called a database handler. This is a class you’d use as a helper to read and write data to the to-do database. And to do that, under your DBResponse NamedTuple class, you create another class and call it DatabaseHandler.

04:32 And with this, you define an initializer. What you’ll expect when you initialize an instance of this class is a db_path, which is a type of Pathlib Path.

04:43 To differentiate this, you can use an .self .db_path, signifying that this is an internal variable.

04:52 Next, you’ll be creating two methods. One to read to-dos from a database and another to write to-dos to the database. So the first one will be the read_todos() method, def read_todos(self), and the response will be a type DBResponse, which has been defined above here.

05:12 And then in a try and except block, it catches an OSError. And if an error occurs while trying to read to-do items from the database, you just return an instance of DBResponse, but now you have no values to return.

05:26 So you return an empty list for the to-do list and a DB_READ_ERROR. So ensure you imported that from rp_todo and then return DB_READ_ERROR.

05:39 Now in the main try block using context management with .self._db_path, which is your database open method in read mode as db, then you’re going to try to read the values from the file and return to the caller of this method.

05:58 And to do that, you’d use another try and except block. This time you’re reading a JSON file, so the error you can expect is a JSONDecodeError.

06:07 At this point, you can import json from the Python standard library and then catch that error. And if an error occurs while trying to read the JSON file, you’d return again an instance of DBResponse since no file was read, return an empty list and JSON_ERROR, again, import this from rp_todo.

06:29 But if you’re able to read and decode the JSON file, you’d return that to the caller of this method, return an instance of DBResponse, but now read the value using json.load() returning the JSON file, which in the context management is referred to as db and then the code would be SUCCESS.

06:50 Great. You’ve just defined your read_todos() method, which tries to parse the JSON file, read the data, and return to the caller whilst catching appropriate errors if they occur.

07:01 Next will be a method to write to-dos to the database. You define the method, write_todos(), reference to class self and this method takes a list of to-dos type annotated as lists with values that are dictionaries with key str, and value of data type Any.

07:23 This would return an instance of DBResponse.

07:28 Also in a try and except block, this is a file manipulation, so it’d catch an OSError that happens, return an instance of DBResponse knowing there was an error, return back a to-do list path to be written alongside a DB_WRITE_ERROR.

07:46 Back in the try block, using context management with with statement, open up the database, which is the JSON file, using the open method in write mode this time. Reference the context as db and in the context, what you’re going to do is write the list to the JSON file.

08:04 And to do that, you can use json.dump() and what you’re dumping is the to-do list passed to the method, the file to write the data and the little styling indent 4.

08:16 And if all this goes according to plan, you’d return, also give your response the to-do list passed and SUCCESS. Wonderful. You’ve now created the write_todos() method.

08:30 It takes a list of to-dos containing dictionaries of key strings and value of any data type and this will try to write those into the db, which is your JSON file.

08:43 Now, to connect this database handler with your CLI application, you’d write another class. First, open the rptodo file and here you’d create another class, the DatabaseHandler with your CLI application.

08:58 This class will be called Todoer. To create the connection, remember to import the DatabaseHandler. From rptodo database import DatabaseHandler, which you’ve just created in a database.py module.

09:15 Then you go ahead to define the class initializer,

09:18 this reference self. And for initialization, you’d require a db_path, which would be a type of Pathlib Path, no values are returned.

09:29 Then you attach the DatabaseHandler to this Todoer class using method, .self. internal method or internal object DatabaseHandler equals reference the DatabaseHandler you just imported from rptodo database module.

09:48 And this also requires a db_path. So you pass in the db_path gotten during the class or instance initialization. What you’ve just done in object-oriented programming is called class composition, where you’ve made the DatabaseHandler class a part of the Todoer class.

10:08 Class composition is a design principle where a class is built from other classes as components. Instead of inheriting from a parent class, a class has a relationship with other classes.

10:20 It represents a “has-a” or “part-of” relationship instead of inheritance’s “is-a” relationship. So unlike inheritance where you say a class is a type of another class, in composition, you just say a class is part of another class.

10:38 In this lesson, you put together a lot of things that shape how your to-do application’s backend will work. You started out with deciding what data structure you’d use to store your to-do item,

10:50 and now you’ve defined utility classes that will allow you to operate on your to-do database, which means you are almost ready to start receiving to-do items and populating them in your database.

11:01 And you do just that in the next lesson.

Become a Member to join the conversation.