tina4-python 3.13.58__tar.gz → 3.13.60__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.13.58 → tina4_python-3.13.60}/PKG-INFO +5 -5
- {tina4_python-3.13.58 → tina4_python-3.13.60}/README.md +4 -4
- {tina4_python-3.13.58 → tina4_python-3.13.60}/pyproject.toml +1 -1
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/CLAUDE.md +5 -5
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/ai/__init__.py +84 -17
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/connection.py +27 -10
- {tina4_python-3.13.58 → tina4_python-3.13.60}/.gitignore +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/env.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/Attachment.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/Channel.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/ChannelMember.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/Message.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/Workspace.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/models/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/realtime/storage.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.58 → tina4_python-3.13.60}/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.13.
|
|
3
|
+
Version: 3.13.60
|
|
4
4
|
Summary: Tina4 Python v3 — Zero-dependency, lightweight web framework
|
|
5
5
|
Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -259,7 +259,7 @@ from tina4_python.core.router import get, post, noauth
|
|
|
259
259
|
@get("/api/users")
|
|
260
260
|
async def list_users(request, response):
|
|
261
261
|
from src.orm.User import User
|
|
262
|
-
return response(User
|
|
262
|
+
return response(User.select(limit=100))
|
|
263
263
|
|
|
264
264
|
@get("/api/users/{id:int}")
|
|
265
265
|
async def get_user(id, request, response):
|
|
@@ -320,7 +320,7 @@ Render it from a route:
|
|
|
320
320
|
@get("/")
|
|
321
321
|
async def home(request, response):
|
|
322
322
|
from src.orm.User import User
|
|
323
|
-
users =
|
|
323
|
+
users = [u.to_dict() for u in User.select(limit=20)]
|
|
324
324
|
return response.render("pages/home.twig", {"title": "Users", "users": users})
|
|
325
325
|
```
|
|
326
326
|
|
|
@@ -499,7 +499,7 @@ Auto-generated at `/swagger`:
|
|
|
499
499
|
@tags(["users"])
|
|
500
500
|
@get("/api/users")
|
|
501
501
|
async def users(request, response):
|
|
502
|
-
return response(User
|
|
502
|
+
return response(User.select())
|
|
503
503
|
```
|
|
504
504
|
|
|
505
505
|
### Event System
|
|
@@ -661,7 +661,7 @@ profile = user.has_one("Profile", "user_id")
|
|
|
661
661
|
customer = order.belongs_to("Customer", "customer_id")
|
|
662
662
|
|
|
663
663
|
# Eager loading with include=
|
|
664
|
-
users = User
|
|
664
|
+
users = User.select(include=["orders", "profile"])
|
|
665
665
|
```
|
|
666
666
|
|
|
667
667
|
### DB Query Caching
|
|
@@ -227,7 +227,7 @@ from tina4_python.core.router import get, post, noauth
|
|
|
227
227
|
@get("/api/users")
|
|
228
228
|
async def list_users(request, response):
|
|
229
229
|
from src.orm.User import User
|
|
230
|
-
return response(User
|
|
230
|
+
return response(User.select(limit=100))
|
|
231
231
|
|
|
232
232
|
@get("/api/users/{id:int}")
|
|
233
233
|
async def get_user(id, request, response):
|
|
@@ -288,7 +288,7 @@ Render it from a route:
|
|
|
288
288
|
@get("/")
|
|
289
289
|
async def home(request, response):
|
|
290
290
|
from src.orm.User import User
|
|
291
|
-
users =
|
|
291
|
+
users = [u.to_dict() for u in User.select(limit=20)]
|
|
292
292
|
return response.render("pages/home.twig", {"title": "Users", "users": users})
|
|
293
293
|
```
|
|
294
294
|
|
|
@@ -467,7 +467,7 @@ Auto-generated at `/swagger`:
|
|
|
467
467
|
@tags(["users"])
|
|
468
468
|
@get("/api/users")
|
|
469
469
|
async def users(request, response):
|
|
470
|
-
return response(User
|
|
470
|
+
return response(User.select())
|
|
471
471
|
```
|
|
472
472
|
|
|
473
473
|
### Event System
|
|
@@ -629,7 +629,7 @@ profile = user.has_one("Profile", "user_id")
|
|
|
629
629
|
customer = order.belongs_to("Customer", "customer_id")
|
|
630
630
|
|
|
631
631
|
# Eager loading with include=
|
|
632
|
-
users = User
|
|
632
|
+
users = User.select(include=["orders", "profile"])
|
|
633
633
|
```
|
|
634
634
|
|
|
635
635
|
### DB Query Caching
|
|
@@ -24,14 +24,14 @@ Every piece of repeated logic must be centralised. If two routes format a date,
|
|
|
24
24
|
```python
|
|
25
25
|
@get("/api/orders")
|
|
26
26
|
async def list_orders(request, response):
|
|
27
|
-
orders =
|
|
27
|
+
orders = [o.to_dict() for o in Order.select()]
|
|
28
28
|
for o in orders:
|
|
29
29
|
o["total"] = f"${abs(float(o['total'])):,.2f}" # duplicated
|
|
30
30
|
return response(orders)
|
|
31
31
|
|
|
32
32
|
@get("/api/invoices")
|
|
33
33
|
async def list_invoices(request, response):
|
|
34
|
-
invoices =
|
|
34
|
+
invoices = [i.to_dict() for i in Invoice.select()]
|
|
35
35
|
for i in invoices:
|
|
36
36
|
i["amount"] = f"${abs(float(i['amount'])):,.2f}" # duplicated!
|
|
37
37
|
return response(invoices)
|
|
@@ -890,8 +890,8 @@ user.save()
|
|
|
890
890
|
# Delete
|
|
891
891
|
user.delete()
|
|
892
892
|
|
|
893
|
-
# Query
|
|
894
|
-
result = User
|
|
893
|
+
# Query (WHERE fragment -> use where(); select() takes FULL SQL, not a fragment)
|
|
894
|
+
result = User.where("name = ?", params=["Alice"], limit=10)
|
|
895
895
|
|
|
896
896
|
# Convert to dict
|
|
897
897
|
user.to_dict()
|
|
@@ -1797,7 +1797,7 @@ uv run tina4python migrate
|
|
|
1797
1797
|
```python
|
|
1798
1798
|
@get("/api/products")
|
|
1799
1799
|
async def list_products(request, response):
|
|
1800
|
-
return response(Product
|
|
1800
|
+
return response(Product.select(limit=100))
|
|
1801
1801
|
|
|
1802
1802
|
@post("/api/products")
|
|
1803
1803
|
async def create_product(request, response):
|
|
@@ -36,6 +36,76 @@ AI_TOOLS = [
|
|
|
36
36
|
]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
# ── Skill files (the SKILL.md system) ───────────────────────────────────────
|
|
40
|
+
# `tina4 ai` installs the actual skills — not just a CLAUDE.md pointer to them —
|
|
41
|
+
# into BOTH the project (.claude/skills, so they travel with the repo) and the
|
|
42
|
+
# user's global ~/.claude/skills (so they're available in every project). A
|
|
43
|
+
# Python project needs the python developer skill plus the two shared skills;
|
|
44
|
+
# all three are served from the tina4-python release tag.
|
|
45
|
+
_DEV_SKILL = "tina4-developer-python"
|
|
46
|
+
_SKILL_REPO = "tina4-python"
|
|
47
|
+
_SKILLS = {
|
|
48
|
+
_DEV_SKILL: ["auth-and-services.md", "data-and-orm.md", "deployment.md",
|
|
49
|
+
"routes-and-api.md", "templates-and-frontend.md", "realtime.md"],
|
|
50
|
+
"tina4-js": ["html-and-components.md", "signals-and-reactivity.md",
|
|
51
|
+
"persistence.md", "rtc.md"],
|
|
52
|
+
"tina4-maintainer": ["cli-and-deployment.md", "frond-and-frontend.md",
|
|
53
|
+
"routing-and-orm.md", "subsystems.md"],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _skills_ref() -> str:
|
|
58
|
+
"""Release tag to pull skills from — the installed framework version,
|
|
59
|
+
overridable with TINA4_SKILLS_REF (e.g. to test a branch)."""
|
|
60
|
+
ref = os.environ.get("TINA4_SKILLS_REF")
|
|
61
|
+
if ref:
|
|
62
|
+
return ref
|
|
63
|
+
try:
|
|
64
|
+
from tina4_python import __version__
|
|
65
|
+
return __version__
|
|
66
|
+
except Exception:
|
|
67
|
+
return "main"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _fetch_bytes(url: str) -> "bytes | None":
|
|
71
|
+
import urllib.error
|
|
72
|
+
import urllib.request
|
|
73
|
+
try:
|
|
74
|
+
with urllib.request.urlopen(url, timeout=15) as resp:
|
|
75
|
+
return resp.read()
|
|
76
|
+
except (urllib.error.URLError, OSError):
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def install_skills(root: str = ".", targets: "list[Path] | None" = None) -> list[str]:
|
|
81
|
+
"""Install the Tina4 SKILL.md skills into the project AND global
|
|
82
|
+
~/.claude/skills, fetched from the release tag matching this framework
|
|
83
|
+
version. Returns the skills fully installed. Network-dependent."""
|
|
84
|
+
ref = _skills_ref()
|
|
85
|
+
base = f"https://raw.githubusercontent.com/tina4stack/{_SKILL_REPO}/{ref}/.claude/skills"
|
|
86
|
+
if targets is None:
|
|
87
|
+
targets = [Path(root).resolve() / ".claude" / "skills",
|
|
88
|
+
Path.home() / ".claude" / "skills"]
|
|
89
|
+
installed = []
|
|
90
|
+
for skill, refs in _SKILLS.items():
|
|
91
|
+
skill_ok = True
|
|
92
|
+
for dest in targets:
|
|
93
|
+
skill_dir = dest / skill
|
|
94
|
+
(skill_dir / "references").mkdir(parents=True, exist_ok=True)
|
|
95
|
+
data = _fetch_bytes(f"{base}/{skill}/SKILL.md")
|
|
96
|
+
if data is None:
|
|
97
|
+
skill_ok = False
|
|
98
|
+
continue
|
|
99
|
+
(skill_dir / "SKILL.md").write_bytes(data)
|
|
100
|
+
for r in refs:
|
|
101
|
+
rd = _fetch_bytes(f"{base}/{skill}/references/{r}")
|
|
102
|
+
if rd is not None:
|
|
103
|
+
(skill_dir / "references" / r).write_bytes(rd)
|
|
104
|
+
if skill_ok:
|
|
105
|
+
installed.append(skill)
|
|
106
|
+
return installed
|
|
107
|
+
|
|
108
|
+
|
|
39
109
|
def is_installed(root: str, tool: dict) -> bool:
|
|
40
110
|
"""Check if a tool's context file already exists."""
|
|
41
111
|
return (Path(root).resolve() / tool["context_file"]).exists()
|
|
@@ -197,7 +267,7 @@ def _skill_block(context_file: str) -> str:
|
|
|
197
267
|
"## Tina4 Skills\n\n"
|
|
198
268
|
"When working on this Tina4 project, these skills give the "
|
|
199
269
|
"assistant project-aware behaviour:\n\n"
|
|
200
|
-
"- **
|
|
270
|
+
f"- **{_DEV_SKILL}** — Read `.claude/skills/{_DEV_SKILL}/SKILL.md` before building features.\n"
|
|
201
271
|
"- **tina4-js** — Read `.claude/skills/tina4-js/SKILL.md` for frontend work.\n"
|
|
202
272
|
"- **tina4-maintainer** — Read `.claude/skills/tina4-maintainer/SKILL.md` for framework-level changes.\n\n"
|
|
203
273
|
"If Tina4 behaves differently from what these skills describe, that is a bug in the skill. "
|
|
@@ -208,7 +278,7 @@ def _skill_block(context_file: str) -> str:
|
|
|
208
278
|
else:
|
|
209
279
|
body = (
|
|
210
280
|
"Tina4 Skills — read these files before working on this project:\n"
|
|
211
|
-
" .claude/skills/
|
|
281
|
+
f" .claude/skills/{_DEV_SKILL}/SKILL.md (feature development)\n"
|
|
212
282
|
" .claude/skills/tina4-js/SKILL.md (frontend / tina4-js)\n"
|
|
213
283
|
" .claude/skills/tina4-maintainer/SKILL.md (framework-level changes)\n"
|
|
214
284
|
"Found a skill that disagrees with how Tina4 actually behaves? Tell the developer,\n"
|
|
@@ -367,21 +437,18 @@ def _install_claude_skills(root: Path) -> list[str]:
|
|
|
367
437
|
rel = str(target.relative_to(root))
|
|
368
438
|
created.append(rel)
|
|
369
439
|
|
|
370
|
-
#
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
rel = str(target_dir.relative_to(root))
|
|
383
|
-
created.append(rel)
|
|
384
|
-
print(f" \033[32m✓\033[0m Updated {rel}")
|
|
440
|
+
# Install the SKILL.md skills into the project AND the user's global
|
|
441
|
+
# ~/.claude/skills, fetched from the release tag matching this framework
|
|
442
|
+
# version. (The previous code copied from framework_root/.claude/skills,
|
|
443
|
+
# which exists only in a dev/editable checkout — NOT in an installed pip
|
|
444
|
+
# package, where .claude/skills sits outside the tina4_python package and
|
|
445
|
+
# is never shipped. So installed users got no skills at all.)
|
|
446
|
+
for skill in install_skills(str(root)):
|
|
447
|
+
created.append(f".claude/skills/{skill}/")
|
|
448
|
+
try:
|
|
449
|
+
print(f" \033[32m✓\033[0m Installed .claude/skills/{skill} (project + global)")
|
|
450
|
+
except UnicodeEncodeError:
|
|
451
|
+
print(f" [OK] Installed .claude/skills/{skill} (project + global)")
|
|
385
452
|
|
|
386
453
|
return created
|
|
387
454
|
|
|
@@ -274,7 +274,8 @@ class Database:
|
|
|
274
274
|
parsed = urlparse(self.url)
|
|
275
275
|
scheme = parsed.scheme.lower()
|
|
276
276
|
|
|
277
|
-
# Handle sqlite
|
|
277
|
+
# Handle sqlite: URLs — see _connection_path for the slash rule
|
|
278
|
+
# (three slashes = relative to cwd, four = absolute).
|
|
278
279
|
if scheme.startswith("sqlite"):
|
|
279
280
|
scheme = "sqlite"
|
|
280
281
|
|
|
@@ -314,16 +315,32 @@ class Database:
|
|
|
314
315
|
return self.url
|
|
315
316
|
|
|
316
317
|
# In-memory forms — passthrough
|
|
317
|
-
|
|
318
|
-
if raw_path in (":memory:", "/:memory:"):
|
|
318
|
+
if self.url in ("sqlite::memory:", "sqlite:///:memory:"):
|
|
319
319
|
return ":memory:"
|
|
320
320
|
|
|
321
|
-
# Strip
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
321
|
+
# Strip the scheme on the RAW url string, NOT via urlparse. urlparse collapses
|
|
322
|
+
# "sqlite:/x" and "sqlite:///x" to the same .path, which loses the distinction
|
|
323
|
+
# between a one-slash ABSOLUTE path and the documented three-slash RELATIVE form —
|
|
324
|
+
# that was the "sqlite:<abspath> silently goes relative" footgun. Mirror the
|
|
325
|
+
# sequential strip php/ruby/nodejs use (strip sqlite:/// then sqlite:// then sqlite:):
|
|
326
|
+
# sqlite:///app.db → "app.db" (three slashes = relative to cwd)
|
|
327
|
+
# sqlite:///data/app.db → "data/app.db"
|
|
328
|
+
# sqlite:////abs/app.db → "/abs/app.db" (four slashes = absolute)
|
|
329
|
+
# sqlite:///C:/Users/x → "C:/Users/x" (Windows absolute)
|
|
330
|
+
# sqlite:/abs/app.db → "/abs/app.db" (one slash = a real absolute path)
|
|
331
|
+
# sqlite://rel/app.db → "rel/app.db" (two-slash legacy = relative)
|
|
332
|
+
# sqlite:app.db → "app.db" (relative)
|
|
333
|
+
url = self.url
|
|
334
|
+
if url.startswith("sqlite:///"):
|
|
335
|
+
stripped = url[len("sqlite:///"):]
|
|
336
|
+
elif url.startswith("sqlite://"):
|
|
337
|
+
stripped = url[len("sqlite://"):]
|
|
338
|
+
elif url.startswith("sqlite:"):
|
|
339
|
+
stripped = url[len("sqlite:"):]
|
|
325
340
|
else:
|
|
326
|
-
stripped =
|
|
341
|
+
stripped = url
|
|
342
|
+
if stripped == ":memory:":
|
|
343
|
+
return ":memory:"
|
|
327
344
|
|
|
328
345
|
# Windows absolute path (drive-letter form): C:/... or C:\...
|
|
329
346
|
is_windows_abs = (
|
|
@@ -332,8 +349,8 @@ class Database:
|
|
|
332
349
|
and stripped[1] == ":"
|
|
333
350
|
and stripped[2] in ("/", "\\")
|
|
334
351
|
)
|
|
335
|
-
# Unix absolute path
|
|
336
|
-
#
|
|
352
|
+
# Unix absolute path — a leading "/" that survived the scheme strip
|
|
353
|
+
# (four-slash "sqlite:////abs" or the one-slash "sqlite:/abs" form).
|
|
337
354
|
is_unix_abs = stripped.startswith("/")
|
|
338
355
|
|
|
339
356
|
if is_windows_abs or is_unix_abs:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.58 → tina4_python-3.13.60}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|