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 integermessage
is a string, anddata
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”
[…] Step 8: Creating strong data models. […]
[…] Step 8: Create strong data models […]
Have they got rid of facts or something?