Skip to content

Commit 2c0a550

Browse files
authored
Merge pull request #16 from LuisLuii/feature/foreign_tree
Feature/foreign tree
2 parents 8ac5a04 + fa7a095 commit 2c0a550

39 files changed

+2330
-733
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ tests/htmlcov
55
/pyproject.toml
66
*.xml
77
*.pyc
8+
workspace.xml
89
*.iml
10+
*.xml
911
*.sh

src/fastapi_quickcrud/crud_router.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def crud_router_builder(
4040
dependencies: Optional[List[callable]] = None,
4141
crud_models: Optional[CRUDModel] = None,
4242
async_mode: Optional[bool] = None,
43+
foreign_include: Optional[any] = None,
4344
sql_type: Optional[SqlType] = None,
4445
**router_kwargs: Any) -> APIRouter:
4546
"""
@@ -108,18 +109,19 @@ def get_transaction_session():
108109

109110
if async_mode is None:
110111
async_mode = inspect.isasyncgen(db_session())
111-
112+
112113
if sql_type is None:
113114
async def async_runner(f):
114115
return [i.bind.name async for i in f()]
116+
115117
try:
116118
if async_mode:
117119
sql_type, = asyncio.get_event_loop().run_until_complete(async_runner(db_session))
118120
else:
119121
sql_type, = [i.bind.name for i in db_session()]
120122
except Exception:
121123
raise RuntimeError("Some unknown problem occurred error, maybe you are uvicorn.run with reload=True. "
122-
"Try declaring sql_type for crud_router_builder yourself using from fastapi_quickcrud.misc.type import SqlType")
124+
"Try declaring sql_type for crud_router_builder yourself using from fastapi_quickcrud.misc.type import SqlType")
123125

124126
if not crud_methods and NO_PRIMARY_KEY == False:
125127
crud_methods = CrudMethods.get_declarative_model_full_crud_method()
@@ -144,9 +146,15 @@ async def async_runner(f):
144146
crud_methods=crud_methods,
145147
exclude_columns=exclude_columns,
146148
sql_type=sql_type,
149+
foreign_include=foreign_include,
147150
exclude_primary_key=NO_PRIMARY_KEY)
148151

149-
crud_service = query_service(model=db_model, async_mode=async_mode)
152+
foreign_table_mapping = {db_model.__tablename__: db_model}
153+
if foreign_include:
154+
for i in foreign_include:
155+
model , _= convert_table_to_model(i)
156+
foreign_table_mapping[model.__tablename__] = i
157+
crud_service = query_service(model=db_model, async_mode=async_mode, foreign_table_mapping=foreign_table_mapping)
150158
# else:
151159
# crud_service = SQLAlchemyPostgreQueryService(model=db_model, async_mode=async_mode)
152160

@@ -372,6 +380,48 @@ def put_many_api(request_response_model: dict, dependencies):
372380
async_mode=async_mode,
373381
response_model=_response_model)
374382

383+
def find_one_foreign_tree_api(request_response_model: dict, dependencies):
384+
_foreign_list_model = request_response_model.get('foreignListModel', None)
385+
for i in _foreign_list_model:
386+
_request_query_model = i["request_query_model"]
387+
_response_model = i["response_model"]
388+
_path = i["path"]
389+
_function_name = i["function_name"]
390+
request_url_param_model = i["primary_key_dataclass_model"]
391+
routes_source.find_one_foreign_tree(path=_path,
392+
request_query_model=_request_query_model,
393+
response_model=_response_model,
394+
request_url_param_model=request_url_param_model,
395+
db_session=db_session,
396+
query_service=crud_service,
397+
parsing_service=result_parser,
398+
execute_service=execute_service,
399+
dependencies=dependencies,
400+
api=api,
401+
function_name=_function_name,
402+
async_mode=async_mode)
403+
404+
def find_many_foreign_tree_api(request_response_model: dict, dependencies):
405+
_foreign_list_model = request_response_model.get('foreignListModel', None)
406+
for i in _foreign_list_model:
407+
_request_query_model = i["request_query_model"]
408+
_response_model = i["response_model"]
409+
_path = i["path"]
410+
_function_name = i["function_name"]
411+
request_url_param_model = i["primary_key_dataclass_model"]
412+
routes_source.find_many_foreign_tree(path=_path,
413+
request_query_model=_request_query_model,
414+
response_model=_response_model,
415+
request_url_param_model=request_url_param_model,
416+
db_session=db_session,
417+
query_service=crud_service,
418+
parsing_service=result_parser,
419+
execute_service=execute_service,
420+
dependencies=dependencies,
421+
api=api,
422+
async_mode=async_mode,
423+
function_name=_function_name)
424+
375425
api_register = {
376426
CrudMethods.FIND_ONE.value: find_one_api,
377427
CrudMethods.FIND_MANY.value: find_many_api,
@@ -385,7 +435,9 @@ def put_many_api(request_response_model: dict, dependencies):
385435
CrudMethods.PATCH_ONE.value: patch_one_api,
386436
CrudMethods.PATCH_MANY.value: patch_many_api,
387437
CrudMethods.UPDATE_ONE.value: put_one_api,
388-
CrudMethods.UPDATE_MANY.value: put_many_api
438+
CrudMethods.UPDATE_MANY.value: put_many_api,
439+
CrudMethods.FIND_ONE_WITH_FOREIGN_TREE.value: find_one_foreign_tree_api,
440+
CrudMethods.FIND_MANY_WITH_FOREIGN_TREE.value: find_many_foreign_tree_api
389441
}
390442
api = APIRouter(**router_kwargs)
391443

src/fastapi_quickcrud/misc/abstract_query.py

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
from .exceptions import UnknownOrderType, UnknownColumn, UpdateColumnEmptyException
1010
from .type import Ordering
11-
from .utils import clean_input_fields
11+
from .utils import clean_input_fields, path_query_builder
1212
from .utils import find_query_builder
1313

1414

1515
class SQLAlchemyGeneralSQLQueryService(ABC):
1616

17-
def __init__(self, *, model, async_mode):
17+
def __init__(self, *, model, async_mode, foreign_table_mapping):
1818

1919
"""
2020
:param model: declarative_base model
@@ -24,24 +24,32 @@ def __init__(self, *, model, async_mode):
2424
self.model = model
2525
self.model_columns = model
2626
self.async_mode = async_mode
27+
self.foreign_table_mapping = foreign_table_mapping
2728

2829
def get_many(self, *,
2930
join_mode,
3031
query,
32+
target_model=None,
33+
abstract_param=None
3134
) -> BinaryExpression:
3235
filter_args = query
3336
limit = filter_args.pop('limit', None)
3437
offset = filter_args.pop('offset', None)
3538
order_by_columns = filter_args.pop('order_by_columns', None)
39+
model = self.model
40+
if target_model:
41+
model = self.foreign_table_mapping[target_model]
3642
filter_list: List[BinaryExpression] = find_query_builder(param=filter_args,
37-
model=self.model_columns)
43+
model=model)
44+
path_filter_list: List[BinaryExpression] = path_query_builder(params=abstract_param,
45+
model=self.foreign_table_mapping)
3846
join_table_instance_list: list = self.get_join_select_fields(join_mode)
3947

40-
model = self.model
48+
4149
if not isinstance(self.model, Table):
4250
model = model.__table__
4351

44-
stmt = select(*[model] + join_table_instance_list).filter(and_(*filter_list))
52+
stmt = select(*[model] + join_table_instance_list).filter(and_(*filter_list+path_filter_list))
4553
if order_by_columns:
4654
order_by_query_list = []
4755

@@ -79,7 +87,6 @@ def get_one(self, *,
7987
model = self.model
8088
if not isinstance(self.model, Table):
8189
model = model.__table__
82-
a = model.c._all_columns
8390
stmt = select(*[model] + join_table_instance_list).where(and_(*filter_list + extra_query_expression))
8491
# stmt = session.query(*[model] + join_table_instance_list).filter(and_(*filter_list + extra_query_expression))
8592
stmt = self.get_join_by_excpression(stmt, join_mode=join_mode)
@@ -130,9 +137,9 @@ def get_join_select_fields(self, join_mode=None):
130137
if 'exclude' in local_reference and local_reference['exclude']:
131138
continue
132139
for column in local_reference['reference_table_columns']:
133-
foreign_name = local_reference['local']['local_column']
140+
foreign_table_name = local_reference['reference']['reference_table']
134141
join_table_instance_list.append(
135-
column.label(foreign_name + '_foreign_____' + str(column).split('.')[1]))
142+
column.label(foreign_table_name + '_foreign_____' + str(column).split('.')[1]))
136143
return join_table_instance_list
137144

138145
def get_join_by_excpression(self, stmt: BinaryExpression, join_mode=None) -> BinaryExpression:
@@ -182,6 +189,28 @@ def model_query(self,
182189
stmt = select(self.model).where(and_(*filter_list))
183190
return stmt
184191

192+
def get_one_with_foreign_pk(self, *,
193+
join_mode,
194+
query,
195+
target_model,
196+
abstract_param=None
197+
) -> BinaryExpression:
198+
model = self.foreign_table_mapping[target_model]
199+
filter_list: List[BinaryExpression] = find_query_builder(param=query,
200+
model=model)
201+
path_filter_list: List[BinaryExpression] = path_query_builder(params=abstract_param,
202+
model=self.foreign_table_mapping)
203+
join_table_instance_list: list = self.get_join_select_fields(join_mode)
204+
205+
if not isinstance(self.model, Table):
206+
model = model.__table__
207+
208+
stmt = select(*[model] + join_table_instance_list).filter(and_(*filter_list + path_filter_list))
209+
210+
stmt = self.get_join_by_excpression(stmt, join_mode=join_mode)
211+
return stmt
212+
213+
185214
# def update(self, *,
186215
# update_args,
187216
# extra_query,
@@ -202,15 +231,16 @@ def model_query(self,
202231

203232
class SQLAlchemyPGSQLQueryService(SQLAlchemyGeneralSQLQueryService):
204233

205-
def __init__(self, *, model, async_mode):
234+
def __init__(self, *, model, async_mode, foreign_table_mapping):
206235

207236
"""
208237
:param model: declarative_base model
209238
:param async_mode: bool
210239
"""
211240
super(SQLAlchemyPGSQLQueryService,
212241
self).__init__(model=model,
213-
async_mode=async_mode)
242+
async_mode=async_mode,
243+
foreign_table_mapping=foreign_table_mapping)
214244
self.model = model
215245
self.model_columns = model
216246
self.async_mode = async_mode
@@ -255,13 +285,14 @@ def upsert(self, *,
255285

256286
class SQLAlchemySQLITEQueryService(SQLAlchemyGeneralSQLQueryService):
257287

258-
def __init__(self, *, model, async_mode):
288+
def __init__(self, *, model, async_mode, foreign_table_mapping):
259289
"""
260290
:param model: declarative_base model
261291
:param async_mode: bool
262292
"""
263293
super().__init__(model=model,
264-
async_mode=async_mode)
294+
async_mode=async_mode,
295+
foreign_table_mapping=foreign_table_mapping)
265296
self.model = model
266297
self.model_columns = model
267298
self.async_mode = async_mode
@@ -276,13 +307,14 @@ def upsert(self, *,
276307

277308
class SQLAlchemyMySQLQueryService(SQLAlchemyGeneralSQLQueryService):
278309

279-
def __init__(self, *, model, async_mode):
310+
def __init__(self, *, model, async_mode, foreign_table_mapping):
280311
"""
281312
:param model: declarative_base model
282313
:param async_mode: bool
283314
"""
284315
super().__init__(model=model,
285-
async_mode=async_mode)
316+
async_mode=async_mode,
317+
foreign_table_mapping=foreign_table_mapping)
286318
self.model = model
287319
self.model_columns = model
288320
self.async_mode = async_mode
@@ -297,13 +329,14 @@ def upsert(self, *,
297329

298330
class SQLAlchemyMariaDBQueryService(SQLAlchemyGeneralSQLQueryService):
299331

300-
def __init__(self, *, model, async_mode):
332+
def __init__(self, *, model, async_mode, foreign_table_mapping):
301333
"""
302334
:param model: declarative_base model
303335
:param async_mode: bool
304336
"""
305337
super().__init__(model=model,
306-
async_mode=async_mode)
338+
async_mode=async_mode,
339+
foreign_table_mapping=foreign_table_mapping)
307340
self.model = model
308341
self.model_columns = model
309342
self.async_mode = async_mode
@@ -318,13 +351,14 @@ def upsert(self, *,
318351

319352
class SQLAlchemyOracleQueryService(SQLAlchemyGeneralSQLQueryService):
320353

321-
def __init__(self, *, model, async_mode):
354+
def __init__(self, *, model, async_mode, foreign_table_mapping):
322355
"""
323356
:param model: declarative_base model
324357
:param async_mode: bool
325358
"""
326359
super().__init__(model=model,
327-
async_mode=async_mode)
360+
async_mode=async_mode,
361+
foreign_table_mapping=foreign_table_mapping)
328362
self.model = model
329363
self.model_columns = model
330364
self.async_mode = async_mode
@@ -339,13 +373,14 @@ def upsert(self, *,
339373

340374
class SQLAlchemyMSSqlQueryService(SQLAlchemyGeneralSQLQueryService):
341375

342-
def __init__(self, *, model, async_mode):
376+
def __init__(self, *, model, async_mode, foreign_table_mapping):
343377
"""
344378
:param model: declarative_base model
345379
:param async_mode: bool
346380
"""
347381
super().__init__(model=model,
348-
async_mode=async_mode)
382+
async_mode=async_mode,
383+
foreign_table_mapping=foreign_table_mapping)
349384
self.model = model
350385
self.model_columns = model
351386
self.async_mode = async_mode
@@ -360,13 +395,14 @@ def upsert(self, *,
360395

361396
class SQLAlchemyNotSupportQueryService(SQLAlchemyGeneralSQLQueryService):
362397

363-
def __init__(self, *, model, async_mode):
398+
def __init__(self, *, model, async_mode, foreign_table_mapping):
364399
"""
365400
:param model: declarative_base model
366401
:param async_mode: bool
367402
"""
368403
super().__init__(model=model,
369-
async_mode=async_mode)
404+
async_mode=async_mode,
405+
foreign_table_mapping=foreign_table_mapping)
370406
self.model = model
371407
self.model_columns = model
372408
self.async_mode = async_mode

0 commit comments

Comments
 (0)