tina4-python 3.10.54__tar.gz → 3.10.55__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.
Files changed (144) hide show
  1. {tina4_python-3.10.54 → tina4_python-3.10.55}/PKG-INFO +1 -1
  2. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/__init__.py +2 -2
  3. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/router.py +55 -6
  4. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/crud/__init__.py +27 -8
  5. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/adapter.py +11 -8
  6. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/connection.py +8 -0
  7. tina4_python-3.10.55/tina4_python/database/mongodb.py +751 -0
  8. {tina4_python-3.10.54 → tina4_python-3.10.55}/.gitignore +0 -0
  9. {tina4_python-3.10.54 → tina4_python-3.10.55}/README.md +0 -0
  10. {tina4_python-3.10.54 → tina4_python-3.10.55}/pyproject.toml +0 -0
  11. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/CLAUDE.md +0 -0
  12. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/HtmlElement.py +0 -0
  13. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/Testing.py +0 -0
  14. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/ai/__init__.py +0 -0
  15. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/api/__init__.py +0 -0
  16. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/auth/__init__.py +0 -0
  17. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/cache/__init__.py +0 -0
  18. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/cli/__init__.py +0 -0
  19. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/container/__init__.py +0 -0
  20. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/__init__.py +0 -0
  21. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/cache.py +0 -0
  22. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/constants.py +0 -0
  23. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/events.py +0 -0
  24. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/middleware.py +0 -0
  25. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/request.py +0 -0
  26. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/response.py +0 -0
  27. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/core/server.py +0 -0
  28. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/__init__.py +0 -0
  29. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/mssql.py +0 -0
  31. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/mysql.py +0 -0
  32. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/odbc.py +0 -0
  33. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/postgres.py +0 -0
  34. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/database/sqlite.py +0 -0
  35. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/debug/__init__.py +0 -0
  36. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/debug/error_overlay.py +0 -0
  37. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/dev_admin/__init__.py +0 -0
  38. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/dev_admin/metrics.py +0 -0
  39. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/dev_reload.py +0 -0
  40. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/dotenv/__init__.py +0 -0
  41. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/frond/FROND.md +0 -0
  42. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/frond/__init__.py +0 -0
  43. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/frond/engine.py +0 -0
  44. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/auth/meta.json +0 -0
  45. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  46. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/database/meta.json +0 -0
  47. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  48. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/error-overlay/meta.json +0 -0
  49. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  50. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/orm/meta.json +0 -0
  51. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  52. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  53. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/queue/meta.json +0 -0
  54. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  55. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/rest-api/meta.json +0 -0
  56. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  57. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/templates/meta.json +0 -0
  58. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  59. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  60. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/graphql/__init__.py +0 -0
  61. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/i18n/__init__.py +0 -0
  62. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/mcp/__init__.py +0 -0
  63. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/mcp/protocol.py +0 -0
  64. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/mcp/tools.py +0 -0
  65. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/messenger/__init__.py +0 -0
  66. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/migration/__init__.py +0 -0
  67. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/migration/runner.py +0 -0
  68. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/orm/__init__.py +0 -0
  69. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/orm/fields.py +0 -0
  70. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/orm/model.py +0 -0
  71. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/css/tina4.css +0 -0
  72. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/css/tina4.min.css +0 -0
  73. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/favicon.ico +0 -0
  74. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/images/logo.svg +0 -0
  75. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  76. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/js/frond.min.js +0 -0
  77. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  78. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/js/tina4.min.js +0 -0
  79. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/js/tina4js.min.js +0 -0
  80. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/swagger/index.html +0 -0
  81. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  82. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/query_builder/__init__.py +0 -0
  83. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/queue/__init__.py +0 -0
  84. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/queue_backends/__init__.py +0 -0
  85. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/queue_backends/kafka_backend.py +0 -0
  86. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/queue_backends/mongo_backend.py +0 -0
  87. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  88. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/__init__.py +0 -0
  89. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  90. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_badges.scss +0 -0
  91. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  92. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_cards.scss +0 -0
  93. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_forms.scss +0 -0
  94. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_grid.scss +0 -0
  95. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_modals.scss +0 -0
  96. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_nav.scss +0 -0
  97. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_reset.scss +0 -0
  98. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_tables.scss +0 -0
  99. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_typography.scss +0 -0
  100. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  101. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/_variables.scss +0 -0
  102. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/base.scss +0 -0
  103. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/colors.scss +0 -0
  104. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/scss/tina4css/tina4.scss +0 -0
  105. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/seeder/__init__.py +0 -0
  106. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/service/__init__.py +0 -0
  107. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/session/__init__.py +0 -0
  108. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/session_handlers/__init__.py +0 -0
  109. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  110. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/session_handlers/redis_handler.py +0 -0
  111. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/session_handlers/valkey_handler.py +0 -0
  112. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/swagger/__init__.py +0 -0
  113. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/components/crud.twig +0 -0
  114. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  115. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  116. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/docker/python/Dockerfile +0 -0
  117. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  118. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/302.twig +0 -0
  119. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/401.twig +0 -0
  120. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/403.twig +0 -0
  121. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/404.twig +0 -0
  122. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/500.twig +0 -0
  123. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/502.twig +0 -0
  124. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/503.twig +0 -0
  125. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/errors/base.twig +0 -0
  126. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/frontend/README.md +0 -0
  127. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/templates/readme.md +0 -0
  128. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/test_client/__init__.py +0 -0
  129. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  130. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  131. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  132. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  133. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  134. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  135. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  136. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  137. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  138. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  139. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  140. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  141. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/validator/__init__.py +0 -0
  142. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/websocket/__init__.py +0 -0
  143. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/websocket/backplane.py +0 -0
  144. {tina4_python-3.10.54 → tina4_python-3.10.55}/tina4_python/wsdl/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.10.54
3
+ Version: 3.10.55
4
4
  Summary: Tina4 for Python — 54 built-in features, zero dependencies
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -8,13 +8,13 @@ Tina4 Python v3.0 — Zero-dependency, lightweight web framework.
8
8
 
9
9
  One import, everything works.
10
10
  """
11
- __version__ = "3.10.54"
11
+ __version__ = "3.10.55"
12
12
 
13
13
  # ── Route decorators ──
14
14
  from tina4_python.core.router import ( # noqa: E402, F401
15
15
  get, post, put, patch, delete, any_method,
16
16
  noauth, secured, cached, middleware, template,
17
- Router,
17
+ Router, RouteGroup,
18
18
  )
19
19
 
20
20
  # ── HTTP Constants ──
@@ -57,6 +57,51 @@ class RouteRef:
57
57
  return self
58
58
 
59
59
 
60
+ class RouteGroup:
61
+ """A group of routes sharing a common prefix and middleware.
62
+
63
+ Passed to the callback in Router.group(). Supports nesting.
64
+
65
+ Usage::
66
+
67
+ Router.group("/api", lambda group: [
68
+ group.get("/users", list_handler),
69
+ group.post("/users", create_handler),
70
+ group.group("/admin", lambda admin: [
71
+ admin.get("/stats", stats_handler),
72
+ ], middleware=[admin_check]),
73
+ ], middleware=[auth_check])
74
+ """
75
+
76
+ def __init__(self, router_cls, prefix: str, middleware: list = None):
77
+ self._router = router_cls
78
+ self._prefix = prefix
79
+ self._middleware = middleware or []
80
+
81
+ def get(self, path: str, handler, **options) -> RouteRef:
82
+ return self._router.add("GET", self._prefix + path, handler, middleware=self._middleware, **options)
83
+
84
+ def post(self, path: str, handler, **options) -> RouteRef:
85
+ return self._router.add("POST", self._prefix + path, handler, middleware=self._middleware, **options)
86
+
87
+ def put(self, path: str, handler, **options) -> RouteRef:
88
+ return self._router.add("PUT", self._prefix + path, handler, middleware=self._middleware, **options)
89
+
90
+ def patch(self, path: str, handler, **options) -> RouteRef:
91
+ return self._router.add("PATCH", self._prefix + path, handler, middleware=self._middleware, **options)
92
+
93
+ def delete(self, path: str, handler, **options) -> RouteRef:
94
+ return self._router.add("DELETE", self._prefix + path, handler, middleware=self._middleware, **options)
95
+
96
+ def any(self, path: str, handler, **options) -> RouteRef:
97
+ return self._router.add("ANY", self._prefix + path, handler, middleware=self._middleware, **options)
98
+
99
+ def group(self, prefix: str, callback, middleware=None):
100
+ merged = list(self._middleware) + (middleware or [])
101
+ nested = RouteGroup(self._router, self._prefix + prefix.rstrip("/"), merged)
102
+ callback(nested)
103
+
104
+
60
105
  class Router:
61
106
  """Route registry and matcher."""
62
107
 
@@ -68,14 +113,17 @@ class Router:
68
113
  def group(cls, prefix: str, callback, middleware=None):
69
114
  """Register routes with a shared prefix and optional middleware.
70
115
 
71
- Saves/restores static prefix and middleware state around the
72
- callback so that nested groups concatenate correctly.
116
+ The callback receives a RouteGroup object with get/post/put/patch/
117
+ delete/any/group methods for registering routes under the prefix.
73
118
 
74
119
  Usage::
75
120
 
76
- Router.group("/api", lambda: [
77
- Router.get("/users", handler),
78
- Router.post("/users", handler),
121
+ Router.group("/api", lambda group: [
122
+ group.get("/users", list_handler),
123
+ group.post("/users", create_handler),
124
+ group.group("/admin", lambda admin: [
125
+ admin.get("/stats", stats_handler),
126
+ ], middleware=[admin_check]),
79
127
  ], middleware=[auth_check])
80
128
  """
81
129
  prev_prefix = cls._group_prefix
@@ -85,7 +133,8 @@ class Router:
85
133
  cls._group_middleware = prev_middleware + (middleware or [])
86
134
 
87
135
  try:
88
- callback()
136
+ group = RouteGroup(cls, cls._group_prefix, list(cls._group_middleware))
137
+ callback(group)
89
138
  finally:
90
139
  cls._group_prefix = prev_prefix
91
140
  cls._group_middleware = prev_middleware
@@ -15,7 +15,7 @@ Discovers ORM models and registers CRUD routes automatically.
15
15
 
16
16
  Generated endpoints per model:
17
17
 
18
- GET /api/{table_name} — list with pagination (limit, skip)
18
+ GET /api/{table_name} — list with pagination (limit, offset; also accepts page, per_page)
19
19
  GET /api/{table_name}/{id} — get single record by primary key
20
20
  POST /api/{table_name} — create new record
21
21
  PUT /api/{table_name}/{id} — update record by primary key
@@ -94,18 +94,37 @@ class AutoCrud:
94
94
  # ── GET /api/{table} — list with pagination ──────────────
95
95
  async def list_handler(request, response, _cls=model_class):
96
96
  try:
97
- limit = int(request.params.get("limit", 10))
98
- skip = int(request.params.get("skip", 0))
97
+ # Primary names: limit / offset
98
+ # Compat names: per_page / page (PHP/Ruby/Node style)
99
+ limit = int(request.params.get("limit", request.params.get("per_page", 10)))
100
+ offset = int(request.params.get("offset", 0))
101
+ # page/per_page compat: if page is provided, derive offset from it
102
+ if "page" in request.params and "offset" not in request.params:
103
+ page = int(request.params.get("page", 1))
104
+ per_page = int(request.params.get("per_page", limit))
105
+ offset = (page - 1) * per_page
106
+ limit = per_page
107
+ else:
108
+ page = (offset // limit) + 1 if limit else 1
99
109
  except (ValueError, TypeError):
100
110
  limit = 10
101
- skip = 0
111
+ offset = 0
112
+ page = 1
102
113
 
103
- records, total = _cls.all(limit=limit, skip=skip)
114
+ records, total = _cls.all(limit=limit, skip=offset)
115
+ total_pages = max(1, -(-total // limit)) if limit else 1
116
+ data = [r.to_dict() for r in records]
104
117
  return response({
105
- "data": [r.to_dict() for r in records],
106
- "total": total,
118
+ "records": data, # standard name
119
+ "data": data, # backwards compat
120
+ "count": total, # standard name
121
+ "total": total, # backwards compat
107
122
  "limit": limit,
108
- "skip": skip,
123
+ "offset": offset,
124
+ "page": page,
125
+ "per_page": limit, # backwards compat
126
+ "totalPages": total_pages, # camelCase standard
127
+ "total_pages": total_pages, # backwards compat
109
128
  })
110
129
 
111
130
  list_handler.__name__ = f"autocrud_list_{table}"
@@ -33,16 +33,19 @@ class DatabaseResult:
33
33
 
34
34
  def to_paginate(self, page: int = 1, per_page: int = 20) -> dict:
35
35
  total_pages = max(1, -(-self.count // per_page)) # ceil division
36
- start = (page - 1) * per_page
37
- end = start + per_page
36
+ offset = (page - 1) * per_page
37
+ data = self.records[offset:offset + per_page]
38
38
  return {
39
- "data": self.records[start:end],
40
- "total": self.count,
39
+ "records": data, # standard name
40
+ "data": data, # backwards compat (PHP/Ruby/Node)
41
+ "count": self.count, # standard name
42
+ "total": self.count, # backwards compat
43
+ "limit": per_page, # standard name
44
+ "offset": offset, # standard name
41
45
  "page": page,
42
- "per_page": per_page,
43
- "total_pages": total_pages,
44
- "has_next": page < total_pages,
45
- "has_prev": page > 1,
46
+ "per_page": per_page, # backwards compat
47
+ "totalPages": total_pages, # camelCase standard
48
+ "total_pages": total_pages, # backwards compat
46
49
  }
47
50
 
48
51
  def column_info(self) -> list[dict]:
@@ -123,6 +123,14 @@ register_driver("sqlserver", MSSQLAdapter)
123
123
  from tina4_python.database.firebird import FirebirdAdapter
124
124
  register_driver("firebird", FirebirdAdapter)
125
125
 
126
+ # Register MongoDB (pymongo — optional)
127
+ try:
128
+ from tina4_python.database.mongodb import MongoDBAdapter
129
+ register_driver("mongodb", MongoDBAdapter)
130
+ register_driver("pymongo", MongoDBAdapter)
131
+ except ImportError:
132
+ pass
133
+
126
134
 
127
135
  class Database:
128
136
  """Database connection manager.