Description
A developer's journey through code. I build, I break, and I write about it. Explore articles on modern software development, programming tips, and more.
The Repository pattern is a software design principle that promotes the separation of business logic from the mechanisms used to retrieve and store data. This separation is achieved by introducing an intermediary layer, the repository, which provides a unified interface for interacting with various data sources.
from abc import ABC, abstractmethod
from sqlmodel import SQLModel, create_engine, Session, Field, select
# Define the model
class Item(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
name: str
# Repository Interface
class IRepository(ABC):
@abstractmethod
def add(self, item: Item):
pass
@abstractmethod
def get(self, name: str) -> Item | None:
pass
# SQLModel implementation
class SQLModelRepository(IRepository):
def __init__(self, db_string="sqlite:///todo.db"):
self.engine = create_engine(db_string)
SQLModel.metadata.create_all(self.engine)
self.session = Session(self.engine)
def add(self, item: Item):
self.session.add(item)
self.session.commit()
def get(self, name: str) -> Item | None:
statement = select(Item).where(Item.name == name)
return self.session.exec(statement).first()
# CSV implementation
class CsvRepository(IRepository):
def __init__(self, file_path="todo.csv"):
self._file_path = file_path
def add(self, item: Item):
with open(self._file_path, "a") as f:
f.write(f"{item.id},{item.name}\n")
def get(self, name: str) -> Item | None:
with open(self._file_path, "r") as f:
return next(
(
Item(id=int(id_str), name=item_name)
for line in f
if (id_str := line.strip().split(",", 1)[0])
and (item_name := line.strip().split(",", 1)[1]) == name
),
None,
)
if __name__ == "__main__":
repo = SQLModelRepository()
repo.add(Item(name="Buy Milk"))
sql_item = repo.get("Buy Milk")
# Swap out the repository implementation
csv_repo = CsvRepository()
csv_repo.add(Item(id=1, name="Buy Milk"))
csv_item = csv_repo.get("Buy Milk")
print(f"{sql_item=}, {csv_item=}, {sql_item == csv_item=}")
# outputs:
# sql_item=Item(name='Buy Milk', id=1), csv_item=Item(id=1, name='Buy Milk'), sql_item == csv_item=True
Let me further explain the code:
Note: for the sake of brevity and clarity in this demonstration, we have omitted explicit error handling mechanisms. In a production environment, robust error handling is important. This includes carefully managing potential issues such as database connectivity problems or file access failures. Implementing proper error handling ensures that the program can recover from unexpected situations and provide informative messages to the user or system administrator.
This illustrative example demonstrates how the repository pattern can be effectively applied in Python. A key benefit of this approach is increased design flexibility. By abstracting data access, we can easily switch between different data storage methods (e.g., databases, files) without impacting the core business logic. This enhances code testability and maintainability.
Have you implemented the repository pattern in your own projects? I will like to know about your experiences in the comments section below.
Cookies improve user experience on SunshineIHCTS. By continuing to use this website, you consent to the use of cookies in accordance with the Privacy policy.
A developer's journey through code. I build, I break, and I write about it. Explore articles on modern software development, programming tips, and more.
Comments section
You need to be logged in to comment, Login or Register.Approved comments:
No comments yet! be the first to comment