tina4-python 3.10.76__tar.gz → 3.10.83__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.
- {tina4_python-3.10.76 → tina4_python-3.10.83}/PKG-INFO +1 -1
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/__init__.py +1 -1
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/server.py +43 -10
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/orm/__init__.py +1 -1
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/orm/fields.py +1 -1
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/orm/model.py +60 -6
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/queue/__init__.py +212 -264
- tina4_python-3.10.83/tina4_python/queue/job.py +51 -0
- tina4_python-3.10.83/tina4_python/queue/lite_backend.py +300 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/websocket/__init__.py +62 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/.gitignore +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/README.md +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/pyproject.toml +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/Testing.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/events.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/request.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/response.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/core/router.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/dev_reload.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.10.76 → tina4_python-3.10.83}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -629,9 +629,24 @@ async def _handle_dev_websocket(reader, writer, headers, path):
|
|
|
629
629
|
|
|
630
630
|
|
|
631
631
|
def _init_session(request: Request) -> None:
|
|
632
|
-
"""Auto-start session from cookie. Modifies request.session in place.
|
|
632
|
+
"""Auto-start session from cookie. Modifies request.session in place.
|
|
633
|
+
|
|
634
|
+
Session creation is skipped for WebSocket upgrade requests — they don't send
|
|
635
|
+
cookies and would create orphaned session files that are never cleaned up.
|
|
636
|
+
A session is only created when:
|
|
637
|
+
- A tina4_session cookie is already present (resume existing session), OR
|
|
638
|
+
- The request is a normal HTTP request (not a WebSocket upgrade)
|
|
639
|
+
"""
|
|
633
640
|
if request.session is not None:
|
|
634
641
|
return
|
|
642
|
+
|
|
643
|
+
# Skip session creation for WebSocket upgrade requests.
|
|
644
|
+
# WebSocket clients don't send cookies so sess.start(None) would create a
|
|
645
|
+
# new orphaned file every connection that is never cleaned up.
|
|
646
|
+
upgrade = request.headers.get("upgrade", "").lower()
|
|
647
|
+
if upgrade == "websocket":
|
|
648
|
+
return
|
|
649
|
+
|
|
635
650
|
try:
|
|
636
651
|
from tina4_python.session import Session
|
|
637
652
|
cookie_header = request.headers.get("cookie", "")
|
|
@@ -641,8 +656,19 @@ def _init_session(request: Request) -> None:
|
|
|
641
656
|
if part.startswith("tina4_session="):
|
|
642
657
|
sid_match = part.split("=", 1)[1]
|
|
643
658
|
break
|
|
644
|
-
|
|
645
|
-
|
|
659
|
+
|
|
660
|
+
# Only create a new session for HTTP requests that don't already have one.
|
|
661
|
+
# This prevents a new empty session file being written on every anonymous request.
|
|
662
|
+
if sid_match is None:
|
|
663
|
+
# Lazy session: attach a Session object but don't persist until the route
|
|
664
|
+
# explicitly writes to it. The session file is only created on first save().
|
|
665
|
+
sess = Session()
|
|
666
|
+
sess.start(None)
|
|
667
|
+
sess._is_new = True # flag for _finalize_response to skip saving if unused
|
|
668
|
+
else:
|
|
669
|
+
sess = Session()
|
|
670
|
+
sess.start(sid_match)
|
|
671
|
+
|
|
646
672
|
request.session = sess
|
|
647
673
|
# Probabilistic garbage collection (1% of requests)
|
|
648
674
|
import random
|
|
@@ -938,15 +964,22 @@ def _finalize_response(
|
|
|
938
964
|
except Exception:
|
|
939
965
|
pass
|
|
940
966
|
|
|
941
|
-
# Session save + cookie
|
|
967
|
+
# Session save + cookie.
|
|
968
|
+
# Skip saving if this was a brand-new session that the route never wrote to —
|
|
969
|
+
# that prevents empty orphaned session files accumulating on disk.
|
|
942
970
|
if request.session is not None:
|
|
943
971
|
try:
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
972
|
+
is_new_and_empty = (
|
|
973
|
+
getattr(request.session, '_is_new', False)
|
|
974
|
+
and not request.session.all()
|
|
975
|
+
)
|
|
976
|
+
if not is_new_and_empty:
|
|
977
|
+
request.session.save()
|
|
978
|
+
sid = request.session.session_id if hasattr(request.session, 'session_id') else getattr(request.session, 'id', None)
|
|
979
|
+
if sid:
|
|
980
|
+
ttl = int(os.environ.get("TINA4_SESSION_TTL", "3600"))
|
|
981
|
+
samesite = os.environ.get("TINA4_SESSION_SAMESITE", "Lax")
|
|
982
|
+
response.header("set-cookie", f"tina4_session={sid}; Path=/; HttpOnly; SameSite={samesite}; Max-Age={ttl}")
|
|
950
983
|
import random
|
|
951
984
|
if random.randint(1, 100) == 1:
|
|
952
985
|
request.session.gc()
|
|
@@ -12,7 +12,7 @@ Active Record ORM with SQL-first paradigm.
|
|
|
12
12
|
|
|
13
13
|
# SQL-first — you write the SQL, ORM maps results
|
|
14
14
|
users = User.select("SELECT * FROM users WHERE active = ?", [1])
|
|
15
|
-
user = User.
|
|
15
|
+
user = User.find_by_id(1)
|
|
16
16
|
user.name = "Updated"
|
|
17
17
|
user.save()
|
|
18
18
|
"""
|
|
@@ -262,7 +262,7 @@ class BelongsToDescriptor(RelationshipDescriptor):
|
|
|
262
262
|
fk_value = getattr(obj, fk, None)
|
|
263
263
|
if fk_value is None:
|
|
264
264
|
return None
|
|
265
|
-
return related_cls.
|
|
265
|
+
return related_cls.find_by_id(fk_value)
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
def has_many(model_name: str, foreign_key: str = None) -> HasManyDescriptor:
|
|
@@ -365,15 +365,69 @@ class ORM(metaclass=ORMMeta):
|
|
|
365
365
|
return cls.select_one(sql, [pk_value], include=include)
|
|
366
366
|
|
|
367
367
|
@classmethod
|
|
368
|
-
def find(cls,
|
|
369
|
-
"""
|
|
370
|
-
return cls.find_by_id(pk_value, include)
|
|
368
|
+
def find(cls, filter: dict = None, limit: int = 100, offset: int = 0, order_by: str = None, include: list[str] = None):
|
|
369
|
+
"""Find records by filter dict.
|
|
371
370
|
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
Usage:
|
|
372
|
+
User.find({"name": "Alice"}) → [User, ...]
|
|
373
|
+
User.find({"age": 18}, limit=10) → [User, ...]
|
|
374
|
+
User.find(order_by="name ASC") → [User, ...]
|
|
375
|
+
User.find() → all records
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
filter: Dict of {column: value} pairs (AND-ed together).
|
|
379
|
+
limit: Max records to return.
|
|
380
|
+
offset: Starting offset.
|
|
381
|
+
order_by: ORDER BY clause (e.g. "name ASC").
|
|
382
|
+
include: Relationship names to eager-load.
|
|
383
|
+
"""
|
|
384
|
+
db = cls._get_db()
|
|
385
|
+
table = cls._get_table()
|
|
386
|
+
conditions = []
|
|
387
|
+
params = []
|
|
388
|
+
|
|
389
|
+
if filter:
|
|
390
|
+
for key, value in filter.items():
|
|
391
|
+
col = cls.field_mapping.get(key, key)
|
|
392
|
+
conditions.append(f"{col} = ?")
|
|
393
|
+
params.append(value)
|
|
394
|
+
|
|
395
|
+
if cls.soft_delete:
|
|
396
|
+
conditions.append("deleted_at IS NULL")
|
|
397
|
+
|
|
398
|
+
sql = f"SELECT * FROM {table}"
|
|
399
|
+
if conditions:
|
|
400
|
+
sql += " WHERE " + " AND ".join(conditions)
|
|
401
|
+
if order_by:
|
|
402
|
+
sql += f" ORDER BY {order_by}"
|
|
403
|
+
|
|
404
|
+
return cls.select(sql, params, limit=limit, offset=offset, include=include)
|
|
405
|
+
|
|
406
|
+
def load(self, filter: str = None, params: list = None, include: list[str] = None) -> bool:
|
|
407
|
+
"""Load a record into this instance.
|
|
408
|
+
|
|
409
|
+
Usage:
|
|
410
|
+
orm.id = 1; orm.load() — uses PK already set
|
|
411
|
+
orm.load("id = ?", [1]) — filter with params
|
|
412
|
+
orm.load("id = 1") — filter string
|
|
374
413
|
|
|
375
414
|
Returns True if a record was found and loaded, False otherwise.
|
|
376
415
|
"""
|
|
416
|
+
db = self._get_db()
|
|
417
|
+
table = self._get_table()
|
|
418
|
+
|
|
419
|
+
if filter is None:
|
|
420
|
+
# No args — use the primary key value already set
|
|
421
|
+
pk = self._get_pk()
|
|
422
|
+
pk_value = getattr(self, pk, None)
|
|
423
|
+
if pk_value is None:
|
|
424
|
+
return False
|
|
425
|
+
pk_col = self.field_mapping.get(pk, self._fields[pk].column)
|
|
426
|
+
sql = f"SELECT * FROM {table} WHERE {pk_col} = ?"
|
|
427
|
+
params = [pk_value]
|
|
428
|
+
else:
|
|
429
|
+
sql = f"SELECT * FROM {table} WHERE {filter}"
|
|
430
|
+
|
|
377
431
|
cls = type(self)
|
|
378
432
|
result = cls.select_one(sql, params, include=include)
|
|
379
433
|
if result is None:
|
|
@@ -614,7 +668,7 @@ class ORM(metaclass=ORMMeta):
|
|
|
614
668
|
fk_value = getattr(self, fk, None)
|
|
615
669
|
if fk_value is None:
|
|
616
670
|
return None
|
|
617
|
-
return related_class.
|
|
671
|
+
return related_class.find_by_id(fk_value)
|
|
618
672
|
|
|
619
673
|
@classmethod
|
|
620
674
|
def _eager_load(cls, instances: list, include: list[str]):
|