Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Where to place CRUD operations? #9

Open
ignacevau opened this issue Nov 9, 2022 · 2 comments
Open

Where to place CRUD operations? #9

ignacevau opened this issue Nov 9, 2022 · 2 comments

Comments

@ignacevau
Copy link

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.

  1. No separate crud operations - would lead to duplicated code.
  2. Following @tiangolo's full stack example structure, there is a separate crud folder. I could create a crud folder for each module.
  3. 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.

@zhanymkanov
Copy link
Owner

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.

@ThirVondukr
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants