66from sqlalchemy .ext .asyncio import AsyncSession
77from sqlalchemy .engine .row import Row
88
9- from .helper import _extract_matching_columns_from_schema , _extract_matching_columns_from_kwargs , _extract_matching_columns_from_column_names
9+ from .helper import _extract_matching_columns_from_schema , _extract_matching_columns_from_kwargs
1010
1111ModelType = TypeVar ("ModelType" )
1212CreateSchemaType = TypeVar ("CreateSchemaType" , bound = BaseModel )
1515DeleteSchemaType = TypeVar ("DeleteSchemaType" , bound = BaseModel )
1616
1717class CRUDBase (Generic [ModelType , CreateSchemaType , UpdateSchemaType , UpdateSchemaInternalType , DeleteSchemaType ]):
18+ """
19+ Base class for CRUD operations on a model.
20+
21+ Parameters
22+ ----------
23+ model : Type[ModelType]
24+ The SQLAlchemy model type.
25+ """
1826 def __init__ (self , model : Type [ModelType ]) -> None :
1927 self ._model = model
2028
2129 async def create (
22- self , db : AsyncSession , object : CreateSchemaType
30+ self ,
31+ db : AsyncSession ,
32+ object : CreateSchemaType
2333 ) -> ModelType :
34+ """
35+ Create a new record in the database.
36+
37+ Parameters
38+ ----------
39+ db : AsyncSession
40+ The SQLAlchemy async session.
41+ object : CreateSchemaType
42+ The Pydantic schema containing the data to be saved.
43+
44+ Returns
45+ -------
46+ ModelType
47+ The created database object.
48+ """
2449 object_dict = object .model_dump ()
2550 db_object = self ._model (** object_dict )
2651 db .add (db_object )
2752 await db .commit ()
2853 return db_object
2954
30- async def get (self , db : AsyncSession , schema_to_select : Type [BaseModel ] | None = None , ** kwargs ) -> ModelType | None :
55+ async def get (
56+ self ,
57+ db : AsyncSession ,
58+ schema_to_select : Type [BaseModel ] | None = None ,
59+ ** kwargs
60+ ) -> ModelType | None :
61+ """
62+ Fetch a single record based on filters.
63+
64+ Parameters
65+ ----------
66+ db : AsyncSession
67+ The SQLAlchemy async session.
68+ schema_to_select : Type[BaseModel] | None, optional
69+ Pydantic schema for selecting specific columns. Default is None to select all columns.
70+ kwargs : dict
71+ Filters to apply to the query.
72+
73+ Returns
74+ -------
75+ ModelType | None
76+ The fetched database row or None if not found.
77+ """
3178 to_select = _extract_matching_columns_from_schema (model = self ._model , schema = schema_to_select )
3279 stmt = select (* to_select ) \
3380 .filter_by (** kwargs )
3481
3582 result = await db .execute (stmt )
3683 return result .first ()
3784
38- async def get_multi (self , db : AsyncSession , offset : int = 0 , limit : int = 100 , schema_to_select : Type [BaseModel ] | None = None , ** kwargs ) -> List [ModelType ]:
85+ async def get_multi (
86+ self ,
87+ db : AsyncSession ,
88+ offset : int = 0 ,
89+ limit : int = 100 ,
90+ schema_to_select : Type [BaseModel ] | None = None ,
91+ ** kwargs
92+ ) -> List [ModelType ]:
93+ """
94+ Fetch multiple records based on filters.
95+
96+ Parameters
97+ ----------
98+ db : AsyncSession
99+ The SQLAlchemy async session.
100+ offset : int, optional
101+ Number of rows to skip before fetching. Default is 0.
102+ limit : int, optional
103+ Maximum number of rows to fetch. Default is 100.
104+ schema_to_select : Type[BaseModel] | None, optional
105+ Pydantic schema for selecting specific columns. Default is None to select all columns.
106+ kwargs : dict
107+ Filters to apply to the query.
108+
109+ Returns
110+ -------
111+ List[ModelType]
112+ List of fetched database rows.
113+ """
39114 to_select = _extract_matching_columns_from_schema (model = self ._model , schema = schema_to_select )
40115 stmt = select (* to_select ) \
41116 .filter_by (** kwargs ) \
@@ -45,7 +120,26 @@ async def get_multi(self, db: AsyncSession, offset: int = 0, limit: int = 100, s
45120 result = await db .execute (stmt )
46121 return result .all ()
47122
48- async def exists (self , db : AsyncSession , ** kwargs ) -> bool :
123+ async def exists (
124+ self ,
125+ db : AsyncSession ,
126+ ** kwargs
127+ ) -> bool :
128+ """
129+ Check if a record exists based on filters.
130+
131+ Parameters
132+ ----------
133+ db : AsyncSession
134+ The SQLAlchemy async session.
135+ kwargs : dict
136+ Filters to apply to the query.
137+
138+ Returns
139+ -------
140+ bool
141+ True if a record exists, False otherwise.
142+ """
49143 to_select = _extract_matching_columns_from_kwargs (model = self ._model , kwargs = kwargs )
50144 stmt = select (* to_select ) \
51145 .filter_by (** kwargs ) \
@@ -54,7 +148,28 @@ async def exists(self, db: AsyncSession, **kwargs) -> bool:
54148
55149 return result .first () is not None
56150
57- async def update (self , db : AsyncSession , object : Union [UpdateSchemaType , Dict [str , Any ]], ** kwargs ) -> ModelType | None :
151+ async def update (
152+ self ,
153+ db : AsyncSession ,
154+ object : Union [UpdateSchemaType , Dict [str , Any ]],
155+ ** kwargs
156+ ) -> None :
157+ """
158+ Update an existing record in the database.
159+
160+ Parameters
161+ ----------
162+ db : AsyncSession
163+ The SQLAlchemy async session.
164+ object : Union[UpdateSchemaType, Dict[str, Any]]
165+ The Pydantic schema or dictionary containing the data to be updated.
166+ kwargs : dict
167+ Filters for the update.
168+
169+ Returns
170+ -------
171+ None
172+ """
58173 if isinstance (object , dict ):
59174 update_data = object
60175 else :
@@ -68,12 +183,51 @@ async def update(self, db: AsyncSession, object: Union[UpdateSchemaType, Dict[st
68183 await db .execute (stmt )
69184 await db .commit ()
70185
71- async def db_delete (self , db : AsyncSession , ** kwargs ):
186+ async def db_delete (
187+ self ,
188+ db : AsyncSession ,
189+ ** kwargs
190+ ) -> None :
191+ """
192+ Delete a record in the database.
193+
194+ Parameters
195+ ----------
196+ db : AsyncSession
197+ The SQLAlchemy async session.
198+ kwargs : dict
199+ Filters for the delete.
200+
201+ Returns
202+ -------
203+ None
204+ """
72205 stmt = delete (self ._model ).filter_by (** kwargs )
73206 await db .execute (stmt )
74207 await db .commit ()
75208
76- async def delete (self , db : AsyncSession , db_row : Row | None = None , ** kwargs ) -> ModelType | None :
209+ async def delete (
210+ self ,
211+ db : AsyncSession ,
212+ db_row : Row | None = None ,
213+ ** kwargs
214+ ) -> None :
215+ """
216+ Soft delete a record if it has "is_deleted" attribute, otherwise perform a hard delete.
217+
218+ Parameters
219+ ----------
220+ db : AsyncSession
221+ The SQLAlchemy async session.
222+ db_row : Row | None, optional
223+ Existing database row to delete. If None, it will be fetched based on `kwargs`. Default is None.
224+ kwargs : dict
225+ Filters for fetching the database row if not provided.
226+
227+ Returns
228+ -------
229+ None
230+ """
77231 db_row = db_row or await self .get (db = db , ** kwargs )
78232 if db_row :
79233 if "is_deleted" in db_row :
0 commit comments