Playing With Beanie and Bunnet
In my last article, I hyped up two Python libraries that make it easier to store and read data objects based on Pydantic models from a Mongo database. This time I want to go beyond the theory and show you how to use them in practice.
Using Beanie or Bunnet allows you to seamlessly map your Pydantic data models directly to MongoDB documents, providing automatic data validation when writing to the database and ensuring data consistency when reading back into your Python objects, simplifying development and reducing boilerplate code for database interactions.
As a natural continuation of my article series, starting from my initial efforts to outline mechanisms for implementing an ODM library similar to Bunnet, I will now move on to the next step and show you how to use these libraries in practice.
Implementing a one-to-many relation
Referring to my earlier metaphors in previous writings, imagine Beanie and Bunnet are two rabbits that have jumped out of a magician’s hat.
In this case, it can be assumed that the magic hat is an entity capable of producing multiple rabbits. Demonstrably, at least two! So we can think of the relationship between the magic hat and the rabbits as a one-to-many relation.
I will use the synchronous version of the ODM (Object-Document Mapper) library, Bunnet, for the examples for now. But all examples will be however easily translatable to Beanie as well.
So let’s define a rabbit then. Note that a rabbit can belong to one hat. But not necessarily, especially if it’s a loose rabbit. Therefore we equip the rabbit with optional link to the hat. It also guarantees that we don’t need to know to which hat the rabbit belongs to when we create it. It can be put in a hat later:
Clearly, the rabbits must come out of somewhere, so let’s define a hat. I want the hat hat to also know its rabbits. Therefore I also equip the hat with an list of rabbits:
I equipped the hat with an optional list of BackLink references to the rabbits. These backlinks are great tools for the magician’s toolbox. They allow to do another quite useful magic trick. Let’s check that out a bit later.
Now let’s create the famous magician’s hat:
Storing the hat:
And then introduce the rabbits, Beanie and Bunnet.
Now place the rabbits inside the hat:
Note that if you use Pylance or something similar for static type checking, you will get a warning that the type of the hat is not assignable to type Link - although it works. To me personally, this kind of warning is a bit annoying.
I was able to satisfy Pylance by adding a helper function that does satisfy the type checker. Define the casting function as follows:
Then, instead of assigning the hat directly to the rabbit, we use the casting function:
Now let’s save the rabbits to the database:
What we have done so far is to create a one-to-many relationship between the hat and the rabbits. We created and saved the hat first, without the rabbits. Then we created two rabits, assigned the hat to each one and saved them.
Doing magic with backlinks
Now this is were the magic happens. Note that when we saved the hat, we didn’t tell MongoDB about what rabbits belong to it. We just told the rabbits about their hat, not the other way round. However, the backlink mechanism is clever enough to make the link bi-directional. It autmatically makes sure that also the hat knows about its rabbits.
Now if we retrieve the hat from the database again, it will know its rabbits already although we didn’t tell it about them before it was saved!
Let’s find hats:
Declaring fetch_links=True
will make sure that the rabbits are fetched alongside the hats.
Without adding this parameter, the rabbits would not be fetched.
Then the returned hats would only contain a list of links to the rabbits.
Then those links would need to be fetched separately.
After fetching the hats with their rabbits, we can verify that the hats (only one in this case) in deed came with their rabbits. So let’s write a function to neatly convert the hats to a JSON string so we can verify the contents of the hats.
We get a json object that looks like this:
[
{
"name": "Houdini's Rabbit Hat",
"magic_power": "Rabbit Conjuring",
"rabbits": ["Beanie", "Bunnet"]
}
]
These examples were written using Bunnet, but they are easily translatable to Beanie. The main difference from a programming perspective is that Beanie requires await keyword before calls. Bunnet is great for scripting and background tasks. Beanie is great for web applications.
There is a lot more to discover in Bunnet and Beanie. And there is a lot to like about these libraries. They are a great tool in your toolbox if you are working with MongoDB and Pydantic. You can write some really nice and clean code with them. And conjure great software solutions.
Talk to you soon!