fh-matui 0.9.5__py3-none-any.whl → 0.9.6__py3-none-any.whl
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.
- fh_matui/__init__.py +1 -1
- fh_matui/_modidx.py +3 -0
- fh_matui/datatable.py +43 -51
- {fh_matui-0.9.5.dist-info → fh_matui-0.9.6.dist-info}/METADATA +1 -1
- fh_matui-0.9.6.dist-info/RECORD +14 -0
- fh_matui-0.9.5.dist-info/RECORD +0 -14
- {fh_matui-0.9.5.dist-info → fh_matui-0.9.6.dist-info}/WHEEL +0 -0
- {fh_matui-0.9.5.dist-info → fh_matui-0.9.6.dist-info}/entry_points.txt +0 -0
- {fh_matui-0.9.5.dist-info → fh_matui-0.9.6.dist-info}/licenses/LICENSE +0 -0
- {fh_matui-0.9.5.dist-info → fh_matui-0.9.6.dist-info}/top_level.txt +0 -0
fh_matui/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.5"
|
fh_matui/_modidx.py
CHANGED
|
@@ -157,7 +157,10 @@ d = { 'settings': { 'branch': 'master',
|
|
|
157
157
|
'fh_matui/datatable.py'),
|
|
158
158
|
'fh_matui.datatable.DataTableResource._wrap_modal': ( 'datatable.html#datatableresource._wrap_modal',
|
|
159
159
|
'fh_matui/datatable.py'),
|
|
160
|
+
'fh_matui.datatable.DataTableResource._wrap_response': ( 'datatable.html#datatableresource._wrap_response',
|
|
161
|
+
'fh_matui/datatable.py'),
|
|
160
162
|
'fh_matui.datatable._action_menu': ('datatable.html#_action_menu', 'fh_matui/datatable.py'),
|
|
163
|
+
'fh_matui.datatable._is_htmx_request': ('datatable.html#_is_htmx_request', 'fh_matui/datatable.py'),
|
|
161
164
|
'fh_matui.datatable._page_size_select': ('datatable.html#_page_size_select', 'fh_matui/datatable.py'),
|
|
162
165
|
'fh_matui.datatable._safe_int': ('datatable.html#_safe_int', 'fh_matui/datatable.py'),
|
|
163
166
|
'fh_matui.datatable._to_dict': ('datatable.html#_to_dict', 'fh_matui/datatable.py'),
|
fh_matui/datatable.py
CHANGED
|
@@ -403,15 +403,22 @@ def _to_dict(obj: Any) -> dict:
|
|
|
403
403
|
return dict(obj)
|
|
404
404
|
|
|
405
405
|
|
|
406
|
+
def _is_htmx_request(req) -> bool:
|
|
407
|
+
"""Check if request is an HTMX partial request."""
|
|
408
|
+
headers = getattr(req, 'headers', {})
|
|
409
|
+
return headers.get('HX-Request') == 'true'
|
|
410
|
+
|
|
411
|
+
|
|
406
412
|
class DataTableResource:
|
|
407
413
|
"""
|
|
408
414
|
🔧 High-level resource that auto-registers all routes for a data table.
|
|
409
415
|
|
|
410
416
|
**Features:**
|
|
417
|
+
- All callbacks receive `request` for multi-tenant support
|
|
411
418
|
- Custom CRUD hooks with `CrudContext` for rich business logic
|
|
412
419
|
- Async/sync hook support for external API integration
|
|
413
420
|
- Auto-refresh table via HX-Trigger after mutations
|
|
414
|
-
-
|
|
421
|
+
- Layout wrapper for full-page (non-HTMX) responses
|
|
415
422
|
|
|
416
423
|
**Auto-registers 3 routes:**
|
|
417
424
|
- `GET {base_route}` → DataTable list view
|
|
@@ -424,11 +431,13 @@ class DataTableResource:
|
|
|
424
431
|
app,
|
|
425
432
|
base_route: str,
|
|
426
433
|
columns: list[dict],
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
434
|
+
# Data callbacks - ALL receive request as first param
|
|
435
|
+
get_all: Callable[[Any], list], # (req) -> list
|
|
436
|
+
get_by_id: Callable[[Any, Any], Any], # (req, id) -> record
|
|
437
|
+
create: Callable[[Any, dict], Any] = None, # (req, data) -> record
|
|
438
|
+
update: Callable[[Any, Any, dict], Any] = None, # (req, id, data) -> record
|
|
439
|
+
delete: Callable[[Any, Any], bool] = None, # (req, id) -> bool
|
|
440
|
+
# Display options
|
|
432
441
|
title: str = "Records",
|
|
433
442
|
row_id_field: str = "id",
|
|
434
443
|
crud_ops: dict = None,
|
|
@@ -440,12 +449,8 @@ class DataTableResource:
|
|
|
440
449
|
on_create: Callable[[CrudContext], dict] = None,
|
|
441
450
|
on_update: Callable[[CrudContext], dict] = None,
|
|
442
451
|
on_delete: Callable[[CrudContext], None] = None,
|
|
443
|
-
#
|
|
444
|
-
|
|
445
|
-
# Request state accessors (for CrudContext)
|
|
446
|
-
get_user: Callable = None,
|
|
447
|
-
get_db: Callable = None,
|
|
448
|
-
get_table: Callable = None,
|
|
452
|
+
# Layout wrapper for full-page responses
|
|
453
|
+
layout_wrapper: Callable[[Any, Any], Any] = None, # (content, req) -> wrapped
|
|
449
454
|
# Custom generators
|
|
450
455
|
id_generator: Callable[[], Any] = None,
|
|
451
456
|
timestamp_fields: dict = None
|
|
@@ -480,13 +485,8 @@ class DataTableResource:
|
|
|
480
485
|
self.on_update_hook = on_update
|
|
481
486
|
self.on_delete_hook = on_delete
|
|
482
487
|
|
|
483
|
-
#
|
|
484
|
-
self.
|
|
485
|
-
|
|
486
|
-
# Request state accessors
|
|
487
|
-
self.get_user = get_user
|
|
488
|
-
self.get_db = get_db
|
|
489
|
-
self.get_table = get_table
|
|
488
|
+
# Layout wrapper
|
|
489
|
+
self.layout_wrapper = layout_wrapper
|
|
490
490
|
|
|
491
491
|
# Generators
|
|
492
492
|
self.id_generator = id_generator
|
|
@@ -510,33 +510,25 @@ class DataTableResource:
|
|
|
510
510
|
return hook(ctx)
|
|
511
511
|
|
|
512
512
|
def _build_context(self, req, record: dict = None, record_id: Any = None) -> CrudContext:
|
|
513
|
-
"""🏗️ Build CrudContext from request
|
|
513
|
+
"""🏗️ Build CrudContext from request."""
|
|
514
514
|
user = None
|
|
515
515
|
db = None
|
|
516
516
|
tbl = None
|
|
517
517
|
|
|
518
|
+
# Extract user from request.state if available
|
|
518
519
|
try:
|
|
519
|
-
if
|
|
520
|
-
user = self.get_user(req)
|
|
521
|
-
elif hasattr(req, 'state') and hasattr(req.state, 'user'):
|
|
520
|
+
if hasattr(req, 'state') and hasattr(req.state, 'user'):
|
|
522
521
|
user = req.state.user
|
|
523
522
|
except AttributeError:
|
|
524
523
|
pass
|
|
525
524
|
|
|
525
|
+
# Extract db from request.state if available
|
|
526
526
|
try:
|
|
527
|
-
if
|
|
528
|
-
db = self.get_db(req)
|
|
529
|
-
elif hasattr(req, 'state') and hasattr(req.state, 'tenant_db'):
|
|
527
|
+
if hasattr(req, 'state') and hasattr(req.state, 'tenant_db'):
|
|
530
528
|
db = req.state.tenant_db
|
|
531
529
|
except AttributeError:
|
|
532
530
|
pass
|
|
533
531
|
|
|
534
|
-
try:
|
|
535
|
-
if self.get_table and callable(self.get_table):
|
|
536
|
-
tbl = self.get_table(req)
|
|
537
|
-
except AttributeError:
|
|
538
|
-
pass
|
|
539
|
-
|
|
540
532
|
return CrudContext(
|
|
541
533
|
request=req,
|
|
542
534
|
user=user,
|
|
@@ -546,6 +538,12 @@ class DataTableResource:
|
|
|
546
538
|
record_id=record_id
|
|
547
539
|
)
|
|
548
540
|
|
|
541
|
+
def _wrap_response(self, content, req):
|
|
542
|
+
"""Wrap content with layout_wrapper if not an HTMX request."""
|
|
543
|
+
if self.layout_wrapper and not _is_htmx_request(req):
|
|
544
|
+
return self.layout_wrapper(content, req)
|
|
545
|
+
return content
|
|
546
|
+
|
|
549
547
|
def _register_routes(self):
|
|
550
548
|
"""Register all data table routes with the app."""
|
|
551
549
|
rt = self.app.route
|
|
@@ -563,18 +561,9 @@ class DataTableResource:
|
|
|
563
561
|
return await self._handle_save(req)
|
|
564
562
|
|
|
565
563
|
def _get_filtered_data(self, req) -> list:
|
|
566
|
-
"""Get all data
|
|
567
|
-
all_data = self.get_all()
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if self.user_filter and callable(self.user_filter):
|
|
571
|
-
filter_criteria = self.user_filter(req)
|
|
572
|
-
if filter_criteria:
|
|
573
|
-
data = [
|
|
574
|
-
row for row in data
|
|
575
|
-
if all(row.get(k) == v for k, v in filter_criteria.items())
|
|
576
|
-
]
|
|
577
|
-
return data
|
|
564
|
+
"""Get all data from user's callback."""
|
|
565
|
+
all_data = self.get_all(req)
|
|
566
|
+
return [_to_dict(item) for item in all_data]
|
|
578
567
|
|
|
579
568
|
def _filter_by_search(self, data: list, search: str) -> list:
|
|
580
569
|
"""Filter data by search term across searchable columns."""
|
|
@@ -643,7 +632,10 @@ class DataTableResource:
|
|
|
643
632
|
)
|
|
644
633
|
|
|
645
634
|
feedback = Div(id=self.feedback_id)
|
|
646
|
-
|
|
635
|
+
content = Div(feedback, table_container)
|
|
636
|
+
|
|
637
|
+
# Wrap with layout if full-page request
|
|
638
|
+
return self._wrap_response(content, req)
|
|
647
639
|
|
|
648
640
|
def _handle_action(self, req):
|
|
649
641
|
"""Handle action route (view/edit/create/delete)."""
|
|
@@ -690,7 +682,7 @@ class DataTableResource:
|
|
|
690
682
|
# Get record for view/edit/delete
|
|
691
683
|
record = None
|
|
692
684
|
if record_id:
|
|
693
|
-
raw_record = self.get_by_id(record_id)
|
|
685
|
+
raw_record = self.get_by_id(req, record_id)
|
|
694
686
|
record = _to_dict(raw_record) if raw_record else None
|
|
695
687
|
|
|
696
688
|
if not record:
|
|
@@ -744,7 +736,7 @@ class DataTableResource:
|
|
|
744
736
|
self.on_delete_hook(ctx)
|
|
745
737
|
else:
|
|
746
738
|
# Default: use delete_fn
|
|
747
|
-
self.delete_fn(record_id)
|
|
739
|
+
self.delete_fn(req, record_id)
|
|
748
740
|
|
|
749
741
|
return self._success_toast("Record deleted successfully.")
|
|
750
742
|
except Exception as e:
|
|
@@ -805,9 +797,9 @@ class DataTableResource:
|
|
|
805
797
|
if self.on_update_hook:
|
|
806
798
|
data = await self._call_hook(self.on_update_hook, ctx)
|
|
807
799
|
if data is not None:
|
|
808
|
-
self.update_fn(record_id, data)
|
|
800
|
+
self.update_fn(req, record_id, data)
|
|
809
801
|
else:
|
|
810
|
-
self.update_fn(record_id, data)
|
|
802
|
+
self.update_fn(req, record_id, data)
|
|
811
803
|
|
|
812
804
|
return self._success_toast("Record updated successfully.")
|
|
813
805
|
else:
|
|
@@ -821,9 +813,9 @@ class DataTableResource:
|
|
|
821
813
|
if self.on_create_hook:
|
|
822
814
|
data = await self._call_hook(self.on_create_hook, ctx)
|
|
823
815
|
if data is not None:
|
|
824
|
-
self.create_fn(data)
|
|
816
|
+
self.create_fn(req, data)
|
|
825
817
|
else:
|
|
826
|
-
self.create_fn(data)
|
|
818
|
+
self.create_fn(req, data)
|
|
827
819
|
|
|
828
820
|
return self._success_toast("Record created successfully.")
|
|
829
821
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
fh_matui/__init__.py,sha256=UvB3IZAWAs7vV6H9dpZvZPryrSNIyF5_bowVS4_YGFA,23
|
|
2
|
+
fh_matui/_modidx.py,sha256=naHwPQ4kCo-5saE_uozmdPA881kp1gnqvKhmaG-Ya-4,23914
|
|
3
|
+
fh_matui/app_pages.py,sha256=Sn9-tgBpaPNbR-0nZtPLoSCmAWLOGB4UQ88IkFvzBRY,10361
|
|
4
|
+
fh_matui/components.py,sha256=KjdTHzWRXpVWBEIGskW1HfhjPpzRYzi6UA_yRjZyMWM,48254
|
|
5
|
+
fh_matui/core.py,sha256=xtVBN8CtC50ZJ4Iu7o-mUhaA87tWdnz8gBfKRk63Zhs,10680
|
|
6
|
+
fh_matui/datatable.py,sha256=x5HgBWksvBiJyRE0Ux7Ht7n1hy0AZttTVXU0mMiZ8Vo,31714
|
|
7
|
+
fh_matui/foundations.py,sha256=b7PnObJpKN8ZAU9NzCm9xpfnHzFjjAROU7E2YvA_tj4,1820
|
|
8
|
+
fh_matui/web_pages.py,sha256=4mF-jpfVcZTVepfQ-aMGgIUp-nBp0YCkvcdsWhUYeaA,34879
|
|
9
|
+
fh_matui-0.9.6.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
10
|
+
fh_matui-0.9.6.dist-info/METADATA,sha256=-tOPC56fiu90DdvaLQt1C7TOoThZyTJ4m3owELT0BUk,10490
|
|
11
|
+
fh_matui-0.9.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
fh_matui-0.9.6.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
|
|
13
|
+
fh_matui-0.9.6.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
|
|
14
|
+
fh_matui-0.9.6.dist-info/RECORD,,
|
fh_matui-0.9.5.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
fh_matui/__init__.py,sha256=iPcoATf7BiWjSu-KocRdM5zFTR4wx4ktCHlGGpvdc1M,23
|
|
2
|
-
fh_matui/_modidx.py,sha256=RTGErPb_KBkO8z8KjcFH3rczKntoTLkHKAK-4a0oax0,23511
|
|
3
|
-
fh_matui/app_pages.py,sha256=Sn9-tgBpaPNbR-0nZtPLoSCmAWLOGB4UQ88IkFvzBRY,10361
|
|
4
|
-
fh_matui/components.py,sha256=KjdTHzWRXpVWBEIGskW1HfhjPpzRYzi6UA_yRjZyMWM,48254
|
|
5
|
-
fh_matui/core.py,sha256=xtVBN8CtC50ZJ4Iu7o-mUhaA87tWdnz8gBfKRk63Zhs,10680
|
|
6
|
-
fh_matui/datatable.py,sha256=DsK73GhB18s7KRk5n_2QU54AuRtfFGTckIVOQ_a8n1w,31632
|
|
7
|
-
fh_matui/foundations.py,sha256=b7PnObJpKN8ZAU9NzCm9xpfnHzFjjAROU7E2YvA_tj4,1820
|
|
8
|
-
fh_matui/web_pages.py,sha256=4mF-jpfVcZTVepfQ-aMGgIUp-nBp0YCkvcdsWhUYeaA,34879
|
|
9
|
-
fh_matui-0.9.5.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
10
|
-
fh_matui-0.9.5.dist-info/METADATA,sha256=dUxJJcfK4MX0RbEdlchOVr2KbHPmn03NVm-nUvlgdcE,10490
|
|
11
|
-
fh_matui-0.9.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
fh_matui-0.9.5.dist-info/entry_points.txt,sha256=zn4CR4gNTiAAxbFsCxHAf2tQhtW29_YOffjbUTgeoWI,38
|
|
13
|
-
fh_matui-0.9.5.dist-info/top_level.txt,sha256=l80d5eoA2ZjqtPYwAorLMS5PiHxUxz3zKzxMJ41Xoso,9
|
|
14
|
-
fh_matui-0.9.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|