Where to place CRUD operations?
ignacevau opened this issue · 2 comments
First of all - thanks for this beautiful repo!
I noticed that there is no mention of where to store any crud operations in the project structure.
I wonder how you would implement these?
Some implementations I've considered.
- No separate crud operations - would lead to duplicated code.
- Following @tiangolo's full stack example structure, there is a separate crud folder. I could create a crud folder for each module.
- Another implementation I've seen is to declare the crud operations directly in the model's Base class. E.g.:
@classmethod
async def create(cls, **kwargs):
obj = cls(**kwargs)
db.add(obj)
try:
await db.commit()
except Exception:
await db.rollback()
raise
return obj
However, this way you don't get any autocompletion.
So far, we're using a separate crud folder for each module, but I would love to hear any other recommendations.
Hey @ignacevau
Thank you for your kind words!
We put all the CRUD-like logic in service.py
where all the business logic with database is stored.
For instance, that's how our service.py
usually looks like:
from datetime import datetime
from typing import Any, Collection, Mapping
from src.database import database, profile
from src.profiles.schemas import ProfileUpdate, ProfileCreate
async def get_profile_by_user_id(user_id: int) -> Mapping[str, Any] | None:
select_query = profile.select().where(profile.c.user_id == user_id)
return await database.fetch_one(select_query)
async def create_profile(profile_data: ProfileCreate) -> Mapping[str, Any]:
select_query = profile.insert().values(**profile_data.dict()).returning(profile)
return await database.fetch_one(select_query)
We try to keep our service functions as clean as possible and let them do one thing only. All the validations should be done one abstraction layer above.
For example, we must be sure username
doesn't cause UniqueViolationError
before sending it to service.create_profile
.
@ignacevau I would avoid adding any methods to your database models (Maybe if they only operate on that specific model it should be ok, but they shouldn't change database state)
As @zhanymkanov said you can make functions and use them to perform CRUD operations, another option is to use class based services.