half-orm-gen 1.0.0a1__tar.gz → 1.0.0a2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {half_orm_gen-1.0.0a1/half_orm_gen.egg-info → half_orm_gen-1.0.0a2}/PKG-INFO +1 -1
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/crud_routes.py +32 -9
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_app/angular.py +355 -89
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_app/svelte.py +263 -52
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_store/base.py +2 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_store/svelte.py +59 -7
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/templates.py +119 -10
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/templates_fastapi.py +73 -10
- half_orm_gen-1.0.0a2/half_orm_gen/version.txt +1 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2/half_orm_gen.egg-info}/PKG-INFO +1 -1
- half_orm_gen-1.0.0a1/half_orm_gen/version.txt +0 -1
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/AUTHORS +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/LICENSE +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/README.md +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/__init__.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/api_routes.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/cli_extension.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_app/__init__.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/gen_store/__init__.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/generate.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffold.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/api_init.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/custom_authorization.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/custom_init.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/custom_middlewares_init.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/custom_routes.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/guards.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/scaffolding/roles_core.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen/tools.py +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen.egg-info/SOURCES.txt +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen.egg-info/dependency_links.txt +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen.egg-info/requires.txt +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/half_orm_gen.egg-info/top_level.txt +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/pyproject.toml +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/setup.cfg +0 -0
- {half_orm_gen-1.0.0a1 → half_orm_gen-1.0.0a2}/tests/test_extension.py +0 -0
|
@@ -58,13 +58,18 @@ def _simple_pk(relation) -> Tuple[str, str, str] | None:
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
def _filter_params_str(all_fields: dict) -> Tuple[str, str]:
|
|
61
|
-
"""Return (filter_params_block, filter_dict_str) for query-param filters.
|
|
61
|
+
"""Return (filter_params_block, filter_dict_str) for query-param filters.
|
|
62
|
+
|
|
63
|
+
All column filters are prefixed with 'ho_col_' to avoid conflicts with
|
|
64
|
+
custom business logic parameters in @api_* decorated methods.
|
|
65
|
+
"""
|
|
62
66
|
lines = []
|
|
63
67
|
dict_items = []
|
|
64
68
|
for fname, fobj in all_fields.items():
|
|
65
69
|
type_str = _py_type_str(fobj.py_type)
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
param_name = f'ho_col_{fname}'
|
|
71
|
+
lines.append(f' {param_name}: Optional[{type_str}] = None,\n')
|
|
72
|
+
dict_items.append(f"'{fname}': {param_name}")
|
|
68
73
|
return ''.join(lines), ', '.join(dict_items)
|
|
69
74
|
|
|
70
75
|
|
|
@@ -309,15 +314,18 @@ def generate_crud_routes(
|
|
|
309
314
|
pk_field, pk_path_type, pk_py_type = pk_cols[0]
|
|
310
315
|
pk_instance_filter = f'{pk_field}=id'
|
|
311
316
|
pk_broadcast_expr = f'result.get("{pk_field}")'
|
|
317
|
+
pk_is_composite = False
|
|
312
318
|
elif len(pk_cols) > 1:
|
|
313
319
|
pk_field = pk_cols[0][0] # first field; used in WS cascade map
|
|
314
320
|
pk_path_type = 'str'
|
|
315
321
|
pk_py_type = 'str'
|
|
316
322
|
_pk_names = [f for f, _, _ in pk_cols]
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
# New format: col1:val1::col2:val2 (parsed and validated by _parse_composite_pk)
|
|
324
|
+
pk_instance_filter = f"**_parse_composite_pk(id, {_pk_names!r})"
|
|
325
|
+
pk_broadcast_expr = f"_format_composite_pk(result, {_pk_names!r})"
|
|
326
|
+
pk_is_composite = True
|
|
319
327
|
else:
|
|
320
|
-
pk_field = pk_path_type = pk_py_type = pk_instance_filter = pk_broadcast_expr = None
|
|
328
|
+
pk_field = pk_path_type = pk_py_type = pk_instance_filter = pk_broadcast_expr = pk_is_composite = None
|
|
321
329
|
|
|
322
330
|
instance = _instance(relation)
|
|
323
331
|
all_fields = getattr(instance, '_ho_fields', {})
|
|
@@ -357,12 +365,17 @@ def generate_crud_routes(
|
|
|
357
365
|
# GET /{pk}
|
|
358
366
|
if pk_info and (module_str, 'GET') not in covered and 'GET' in crud_access:
|
|
359
367
|
handler_name = f'{handler_prefix}_get'
|
|
368
|
+
if pk_is_composite:
|
|
369
|
+
param_type = 'Path' if templates.FRAMEWORK == 'fastapi' else 'Parameter'
|
|
370
|
+
pk_type_annotation = f'Annotated[str, {param_type}(pattern=_COMPOSITE_PK_PATTERN)]'
|
|
371
|
+
else:
|
|
372
|
+
pk_type_annotation = pk_py_type
|
|
360
373
|
handler_blocks.append(templates.CRUD_GET_ONE.format(
|
|
361
374
|
path=base_path,
|
|
362
375
|
handler_name=handler_prefix,
|
|
363
376
|
pk_instance_filter=pk_instance_filter,
|
|
364
377
|
pk_path_type=pk_path_type,
|
|
365
|
-
|
|
378
|
+
pk_type_annotation=pk_type_annotation,
|
|
366
379
|
module_alias=module_alias,
|
|
367
380
|
class_name=relation.__name__,
|
|
368
381
|
out_typedict=out_class,
|
|
@@ -405,12 +418,17 @@ def generate_crud_routes(
|
|
|
405
418
|
put_in_names = [f for f in all_names if f != pk_field and f not in api_excluded]
|
|
406
419
|
decl_blocks.append('\n' + templates.typedict_block(put_in_class, put_in_names, all_fields) + '\n')
|
|
407
420
|
handler_name = f'{handler_prefix}_update'
|
|
421
|
+
if pk_is_composite:
|
|
422
|
+
param_type = 'Path' if templates.FRAMEWORK == 'fastapi' else 'Parameter'
|
|
423
|
+
pk_type_annotation = f'Annotated[str, {param_type}(pattern=_COMPOSITE_PK_PATTERN)]'
|
|
424
|
+
else:
|
|
425
|
+
pk_type_annotation = pk_py_type
|
|
408
426
|
handler_blocks.append(templates.CRUD_PUT.format(
|
|
409
427
|
path=base_path,
|
|
410
428
|
handler_name=handler_prefix,
|
|
411
429
|
pk_instance_filter=pk_instance_filter,
|
|
412
430
|
pk_path_type=pk_path_type,
|
|
413
|
-
|
|
431
|
+
pk_type_annotation=pk_type_annotation,
|
|
414
432
|
module_alias=module_alias,
|
|
415
433
|
class_name=relation.__name__,
|
|
416
434
|
in_typedict=put_in_class,
|
|
@@ -422,12 +440,17 @@ def generate_crud_routes(
|
|
|
422
440
|
|
|
423
441
|
if (module_str, 'DELETE') not in covered and 'DELETE' in crud_access:
|
|
424
442
|
handler_name = f'{handler_prefix}_delete'
|
|
443
|
+
if pk_is_composite:
|
|
444
|
+
param_type = 'Path' if templates.FRAMEWORK == 'fastapi' else 'Parameter'
|
|
445
|
+
pk_type_annotation = f'Annotated[str, {param_type}(pattern=_COMPOSITE_PK_PATTERN)]'
|
|
446
|
+
else:
|
|
447
|
+
pk_type_annotation = pk_py_type
|
|
425
448
|
handler_blocks.append(templates.CRUD_DELETE.format(
|
|
426
449
|
path=base_path,
|
|
427
450
|
handler_name=handler_prefix,
|
|
428
451
|
pk_instance_filter=pk_instance_filter,
|
|
429
452
|
pk_path_type=pk_path_type,
|
|
430
|
-
|
|
453
|
+
pk_type_annotation=pk_type_annotation,
|
|
431
454
|
module_alias=module_alias,
|
|
432
455
|
class_name=relation.__name__,
|
|
433
456
|
access_description=_access_description(crud_access, 'DELETE'),
|