Our DB layer is currently the biggest piece of code judging by the number of lines, without any tendency to stop growing. Let’s refactor it by splitting it up before it gets too big.
There are obviously two (for now) types of items we’re working with; a “donor” and a “donation”. Although we decided that we want to keep them together in a single database that does not mean that the code that manages them needs to be in a single file.
Mixins in Python are a great way to achieve multiple inheritance, split code logically and be one step closer to stop repeating too much of your code.
Create a new file for our DonorMixin
: chalicelib/db_donor.py
. Move the donor related code from db.py
to the newly
created file.
from uuid import uuid4
class DonorMixin:
def donor_signup(self, donor_dict):
uid = str(uuid4()).split("-")[-1]
try:
self._table.put_item(
Item={
"first_name": donor_dict.get("first_name"),
"city": donor_dict.get("city"),
"type": donor_dict.get("type"),
"email": donor_dict.get("email"),
"PK": f"DONOR#{uid}",
}
)
self._logger.debug(
f"Inserted donor '{donor_dict.get('email')}' into DynamoDB table '{self._table}'"
)
return True
except Exception as exc:
self._logger.exception(exc)
We can repeat pretty much everything we did to the donor related code from chalicelib/db.py
. Create chalicelib/db_donation.py
and move all relevant code pieces to it.
from uuid import uuid4
class DonationMixin:
def donation_create(self, donation_dict):
uid = str(uuid4()).split("-")[-1]
try:
self._table.put_item(
Item={
"city": donation_dict.get("city"),
"datetime": donation_dict.get("datetime"),
"address": donation_dict.get("address"),
"PK": f"DONATION#{uid}",
}
)
self._logger.debug(
f"Inserted donation '{donation_dict.get('city')}, {donation_dict.get('address')}' "
f"into DynamoDB table '{self._table}'"
)
return True
except Exception as exc:
self._logger.exception(exc)
Our chalicelib/db.py
should now be much smaller and easier to work with. This is what it looks like after refactoring:
import logging
from os import getenv
import boto3
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
from .db_donation import DonationMixin
from .db_donor import DonorMixin
ENV = getenv("ENV", "dev")
first_name = getenv("WORKSHOP_NAME", "ivica") # replace with your own name of course
logger = logging.getLogger(f"{first_name}-savealife")
_DB = None
TABLE_NAME = getenv("TABLE_NAME")
def get_app_db():
global _DB
if _DB is None:
_DB = SavealifeDB(
table=boto3.resource("dynamodb").Table(TABLE_NAME), logger=logger
)
return _DB
class SavealifeDB(DonorMixin, DonationMixin):
def __init__(self, table, logger):
self._table = table
self._logger = logger
Highlighted lines are responsible for importing and using the mixins we created just a few minutes ago.