Categories
Code

Step 8: Create Strong Data Models

So here we are. We’ve created a REST API adapter! We’ve refactored our code making it DRY, added Exception handling, created a new Result model, added logging and helpful comments. And things were good.

But we can do better… How so?

Well, right now, our REST adapter only really helps us with setup as well as HTTP requests like GETs, POSTs, DELETEs, and any basic error handling. But it doesn’t really help us with the data we get back from TheCatAPI.

Sure, you can manually access all the values in the Lists and Dictionaries if you know the magic strings, but do you really want to memorize all those magic strings? I sure don’t!

How do we solve this? With Strong Data Models, of course!

What’s a data model? Well, let’s start with the one we already created. Pop open the models.py file and look at the Results class.

class Result:
    def __init__(self, status_code: int, message: str = '', data: List[Dict] = None):
        """
        Result returned from low-level RestAdapter
        :param status_code: Standard HTTP Status code
        :param message: Human readable result
        :param data: Python List of Dictionaries (or maybe just a single Dictionary on error)
        """
        self.status_code = int(status_code)
        self.message = str(message)
        self.data = data if data else []

In this case, the Result data model holds 3 values and each value is strongly typed.

  • status_code is an integer
  • message is a string, and
  • data is Python object (likely a List of Dictionaries). But we manually populated these fields in our REST adapter.

Let’s find a way to (magically) de-serialize JSON directly into strongly typed Python objects, using features built right into Python!

Let’s start with model Fact type in theCatAPI: https://docs.thecatapi.com/api-reference/models/fact

We can see that Fact is a singular object that has the following fields:

id (string)
text (string)
language_code (string)
breed_id (string)

A sample JSON object would look like this:

{
 'id':'1',
 'text':'A cat has 9 lives',
 'language_code': 'en_US',
 'breed_id': '7'
}

How do we make a Python data model class that is compatible with this JSON object? Well, we map the key values to the parameter names. Like this:

class Fact:
    def __init__(self, id: str, text: str, language_code: str, breed_id: str):
        self.id = id
        self.text = text
        self.language_code = language_code
        self.breed_id = breed_id

Ok, we have a simple data model now. How do we magically get the data from theCatAPI endpoint into this object? Let’s write some code!

from thecatapi.rest_adapter import RestAdapter
from thecatapi.models import Result, Fact
result = catapi.get("/catfact/random/")
catfactdata = result.data
print(catfactdata['text'])


A cat has 9 lives

Let’s go over what just happened:
Lines 1 and 2 import the RestAdapter and our two data models: Result and Fact.
Line 3 GETs a random catfact and stuffs the response into a Result object.
Line 4 and 5 gets the data dictionary and prints the value associated with the magic key "text" found in the data dictionary.

Now that’s okay, but that assumes that you, as the programmer, remember what the cat fact string is named in the dictionary. And this doesn’t use our strong data type: Fact

Let’s try again:

from thecatapi.rest_adapter import RestAdapter
from thecatapi.models import Result, Fact
result = catapi.get("/catfact/random/")
catfact = Fact(**result.data)
print(catfact.text)

A cat has 9 lives

Line 4 leverages the double-star (**) Python operator which unpacks the data dictionary and passes the key/value pairs contained within as key-word arguments to the parameters of the Fact class constructor __init__().

The dictionary has an id key, so does the Fact class constructor in its parameter list. The dictionary has a text key, same with Fact class. (If you hadn’t noticed when we typed in: print(catfact.text)— And same for language_code and breed_id.

Essentially we copied the whole result.data dictionary right into the strongly typed Fact class constructor and now have a Fact object!

At the moment you hit the period key, PyCharm should have displayed a list of all properties it knew about the object catfact — And sure enough text, breed_id, language_code, and id were all listed there available for you to pick. No need to memorize any magic dictionary keys! That’s a win.

Step 9: More complex data models


Source code: https://github.com/PretzelLogix/py-cat-api/tree/08_data_models

3 replies on “Step 8: Create Strong Data Models”

Leave a Reply

Your email address will not be published. Required fields are marked *