This article discusses how manually configuring relationships in SQLAlchemy can make querying easier, using a scenario involving three database tables: Application, ApplicationRunner, and ApplicationLog.
Abstract
In this article, the author shares their experience with using SQLAlchemy in Python for managing database relationships. They compare the manual configuration approach to the automatic handling by SQLAlchemy, discussing the advantages and disadvantages of each method. The article provides examples of how to create one-to-one and one-to-many relationships between the Application, ApplicationRunner, and ApplicationLog tables using manual configurations. The author concludes that while manual configurations can be more complicated and time-consuming, they offer more control and transparency to developers.
Bullet points
The author previously wrote an article about leveraging the power of Entity Framework to minimize complicated Linq queries.
In this scenario, there are three database tables: Application, ApplicationRunner, and ApplicationLog.
The Application table contains a foreign key called ApplicationRunnerId, creating a one-to-one relationship between Application and ApplicationRunner tables.
The ApplicationLog table holds a foreign key to the Application table, specifying a one-to-many relationship.
The article compares the manual configuration of table relationships to the automatic handling by SQLAlchemy.
Manual configurations are created using a code-first approach, defining all table attributes and relationships in Python code.
The author provides examples of creating one-to-one and one-to-many relationships between tables using manual configurations.
The article discusses the pros and cons of manual configurations, including their complexity and the additional control they provide to developers.
The author plans to explore performance questions related to SQLAlchemy and table relationship configurations in a future post.
A couple of weeks ago, I wrote an article about leveraging the power of Entity Framework to minimize having to do complicated Linq queries. Essentially, the solution was to create configurations for each table in the database and then define the relationship between the tables within the configuration. It wasn’t exactly an easy task. In fact, it was a little confusing. But it got me thinking if something similar could be accomplished using SQLAlchemy for Python.
The Situation
In our scenario, we have a database containing three tables, Application, ApplicationRunner, and ApplicationLog. The Application table contains a foreign key called ApplicationRunnerId. This key creates a one-to-one relationship between the Application and ApplicationRunner tables. As for the ApplicationLog table, it holds a foreign key to the Application table specifying a one-to-many relationship.
Taking a look at our current code, all of the heavy lifting of determining table attributes and relationships is being automatically handled by SQLAlchemy.
As for getting the data, we have to either manually query each table, or use a join method. For the sake of simplicity, we are manually querying each table.
What Can Be Done Differently
Instead of being lazy and letting SQLAlchemy do all the hard work, we can take more of a code-first approach and create manual configurations for our tables.
Before we can start modifying the table classes, there are a few extra things we need to import. The first will allow us to define different types for the table attributes, while the second gives us the functionality to create relationships between tables.
Once imported, we can start working on the Application table by defining all the attributes and setting a primary key. The attribute types will match what the field type is in the database (INT will be Integer, VARCHAR will be String, etc.).
Before going any further, we need to create a one-to-one relationship between these two tables. To start off, we will need to add a foreign key to the Application table.
Then, we will add a new class attribute to create a relationship with the ApplicationRunner table. This relationship is special because we will utilize lazy joined loading. Essentially, lazy loading is the objects that are returned from a query without their related objects loaded in. Once the related objects are referenced, another SELECT statement will be run to load in the requested object. As for the “joined”, this adds a JOIN to the initial SELECT to keep everything in the same result set.
The next thing we need to do is add a relationship to the ApplicationRunner table. While doing so, we need to make sure this relationship will back reference the ApplicationRunner table and that it does not need a list of objects since it's a one-to-one relationship.
Now that we have these tables defined and configured, we can move on to the ApplicationLog table. Just like we did with the previous two tables, the attributes and primary key need to be defined first.
Right off the bat, you’ll notice that setting up the configurations takes way longer than having SQLAlchemy automatically handle it for you. Just like in Entity Framework, it can also be a little confusing/complicated to get things up and running. However, this process wasn’t all bad. By configuring the tables through a code-first view, it removes some of the black box that SQLAlchemy can be and give more power back to the developer. With that being said though, it does create more mystery around the querying side since you can’t see the extra joins or selects.
Closing Thoughts
All in all, I thought this was a very enlightening and fun experience. It definitely answered my questions regarding SQLAlchemy’s abilities. However, at the same time, it also brought up some new questions about performance. As someone who writes a lot of APIs where fast requests are a necessity, would this kind of query and configuring be advisable. Let me know what your thoughts are about SQLAlchemy and configuring table relationships within it. In a future post, I plan on exploring these performance questions. Until the next time, happy coding and cheers!