tina4-python 0.2.190__tar.gz → 0.2.191__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-0.2.190 → tina4_python-0.2.191}/PKG-INFO +1 -1
- {tina4_python-0.2.190 → tina4_python-0.2.191}/pyproject.toml +1 -1
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/CLAUDE.md +37 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Database.py +36 -21
- {tina4_python-0.2.190 → tina4_python-0.2.191}/.gitignore +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/README.md +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Api.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Auth.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/CRUD.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Constant.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/DatabaseResult.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/DatabaseTypes.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Debug.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/DevReload.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Env.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/FieldTypes.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Localization.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Messages.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/MiddleWare.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Migration.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/ORM.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Queue.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Request.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Response.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Router.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Session.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/ShellColors.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Swagger.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Template.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Testing.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/WSDL.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Webserver.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/Websocket.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/__init__.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/cli.py +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/messages.pot +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/css/readme.md +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/images/403.png +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/images/404.png +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/images/500.png +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/images/logo.png +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/images/readme.md +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/js/readme.md +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/js/reconnecting-websocket.js +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/js/tina4helper.js +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/templates/readme.md +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
|
@@ -251,6 +251,38 @@ from tina4_python.Queue import Queue, Producer
|
|
|
251
251
|
Producer(Queue(topic="tasks")).produce({"action": "send_email"})
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
+
### 11. Key tina4_python Gotchas
|
|
255
|
+
|
|
256
|
+
1. **Database import**: Use `from tina4_python.Database import Database` (NOT `from tina4_python import Database`)
|
|
257
|
+
2. **noauth/secured import**: Use `from tina4_python import noauth` or `from tina4_python.Router import noauth` (there is NO `tina4_python.Decorators` module)
|
|
258
|
+
3. **Jinja2 template syntax** (common mistakes):
|
|
259
|
+
- **No ternary operator**: Use `{% if x %}...{% endif %}` NOT `{{ x ? 'a' : 'b' }}`
|
|
260
|
+
- **elif not elseif**: Use `{% elif %}` NOT `{% elseif %}`
|
|
261
|
+
- **safe not raw**: Use `{{ var | safe }}` NOT `{{ var | raw }}` for unescaped output
|
|
262
|
+
- **format filter**: Use `{{ "%.2f" | format(value) }}` for number formatting
|
|
263
|
+
- **e() filter has NO arguments**: Use `{{ var|e }}` NOT `{{ var|e('js') }}` — Jinja2's `|e` is HTML-only with no mode parameter (that's PHP Twig syntax)
|
|
264
|
+
- **JS string escaping**: Use `{{ var|replace("'", "\\'") }}` to escape single quotes for inline JS onclick handlers
|
|
265
|
+
- **No `|escape('js')` or `|e('js')`**: These will throw `escape() takes 1 positional argument but 2 were given`
|
|
266
|
+
- **Ternary inline**: Use `{{ 's' if count != 1 else '' }}` NOT `{{ count != 1 ? 's' : '' }}`
|
|
267
|
+
- **Default values**: Use `{{ var|default('fallback') }}` — works on undefined and None
|
|
268
|
+
- **Chaining filters**: `{{ var|default('')|replace("'", "\\'") }}` — left to right
|
|
269
|
+
- **Loop variables**: `loop.index` (1-based), `loop.index0` (0-based), `loop.first`, `loop.last`, `loop.length`
|
|
270
|
+
- **Whitespace control**: `{%- -%}` and `{{- -}}` trim surrounding whitespace
|
|
271
|
+
- **String concatenation**: Use `~` operator: `{{ "hello " ~ name }}` NOT `{{ "hello " + name }}`
|
|
272
|
+
- **include with context**: `{% include "partial.twig" %}` inherits parent context automatically
|
|
273
|
+
- **Macro imports**: `{% from "macros/forms.twig" import field_group %}` — macros do NOT inherit parent context, pass variables explicitly
|
|
274
|
+
4. **DatabaseResult**: `dba.fetch()` returns a `DatabaseResult` object, NOT a plain list
|
|
275
|
+
- Use `.records` to get list of dicts: `result.records`
|
|
276
|
+
- Use `.count` for row count (no `len()` support)
|
|
277
|
+
- Iteration works: `for row in result` or `list(result)`
|
|
278
|
+
- Other methods: `.to_json()`, `.to_array()`, `.to_csv()`, `.to_paginate()`
|
|
279
|
+
4. **fetch_one()**: Returns a plain dict (or None), NOT a DatabaseResult
|
|
280
|
+
5. **Dict access**: All query results use dict access `row["column"]` not attribute access `row.column`
|
|
281
|
+
6. **Firebird connection string**: `firebird.driver:<host>/<port>:<database_path>` — note the `firebird.driver:` prefix (the module name), NOT `firebird:`
|
|
282
|
+
7. **Running the app**: `uv run python app.py <port> <name>` — port and name are CLI args handled by tina4_python
|
|
283
|
+
8. **SCSS**: Files in `src/scss/` are auto-compiled to `src/public/css/` on startup
|
|
284
|
+
|
|
285
|
+
|
|
254
286
|
---
|
|
255
287
|
|
|
256
288
|
## Project Structure
|
|
@@ -668,6 +700,9 @@ except:
|
|
|
668
700
|
db.rollback()
|
|
669
701
|
```
|
|
670
702
|
|
|
703
|
+
**NEVER use `db.execute("COMMIT")` or `db.execute("ROLLBACK")` or `db.execute("BEGIN")`.**
|
|
704
|
+
Always use the proper methods: `db.commit()`, `db.rollback()`, `db.start_transaction()`. Raw SQL transaction commands bypass the framework's connection and transaction state management, leading to unpredictable behaviour and connection leaks.
|
|
705
|
+
|
|
671
706
|
## ORM
|
|
672
707
|
|
|
673
708
|
Define models in `src/orm/` (one class per file, filename matches class name).
|
|
@@ -1310,3 +1345,5 @@ async def dashboard(request, response):
|
|
|
1310
1345
|
</div>
|
|
1311
1346
|
{% endblock %}
|
|
1312
1347
|
```
|
|
1348
|
+
|
|
1349
|
+
|
|
@@ -55,10 +55,13 @@ import datetime
|
|
|
55
55
|
|
|
56
56
|
class Database:
|
|
57
57
|
|
|
58
|
-
def __init__(self, connection_string, username="", password=""):
|
|
58
|
+
def __init__(self, connection_string, username="", password="", charset=""):
|
|
59
59
|
"""
|
|
60
60
|
Initializes a database connection
|
|
61
61
|
:param connection_string:
|
|
62
|
+
:param username:
|
|
63
|
+
:param password:
|
|
64
|
+
:param charset: Character set for the connection (e.g. "UTF8", "WIN1252")
|
|
62
65
|
"""
|
|
63
66
|
# split out the connection string
|
|
64
67
|
# driver:host/port:schema/path
|
|
@@ -71,6 +74,8 @@ class Database:
|
|
|
71
74
|
username = os.environ.get("DATABASE_USERNAME", "")
|
|
72
75
|
if password == "":
|
|
73
76
|
password = os.environ.get("DATABASE_PASSWORD", "")
|
|
77
|
+
if charset == "":
|
|
78
|
+
charset = os.environ.get("DATABASE_CHARSET", "")
|
|
74
79
|
|
|
75
80
|
if connection_string is None:
|
|
76
81
|
from tina4_python import Messages
|
|
@@ -98,6 +103,7 @@ class Database:
|
|
|
98
103
|
self.database_path = params[1]
|
|
99
104
|
self.username = username
|
|
100
105
|
self.password = password
|
|
106
|
+
self.charset = charset
|
|
101
107
|
|
|
102
108
|
if self.database_engine == SQLITE:
|
|
103
109
|
self.dba = self.database_module.connect(self.database_path)
|
|
@@ -149,28 +155,37 @@ class Database:
|
|
|
149
155
|
self.database_path = temp_params[1]
|
|
150
156
|
|
|
151
157
|
if self.database_engine == FIREBIRD:
|
|
152
|
-
|
|
153
|
-
self.host + "/" + str(self.port) + ":" + self.database_path,
|
|
154
|
-
user
|
|
155
|
-
password
|
|
156
|
-
|
|
158
|
+
firebird_args = {
|
|
159
|
+
"dsn": self.host + "/" + str(self.port) + ":" + self.database_path,
|
|
160
|
+
"user": self.username,
|
|
161
|
+
"password": self.password,
|
|
162
|
+
}
|
|
163
|
+
if self.charset:
|
|
164
|
+
firebird_args["charset"] = self.charset
|
|
165
|
+
self.dba = self.database_module.connect(**firebird_args)
|
|
157
166
|
elif self.database_engine == MYSQL:
|
|
158
|
-
|
|
159
|
-
database
|
|
160
|
-
port
|
|
161
|
-
host
|
|
162
|
-
user
|
|
163
|
-
password
|
|
164
|
-
consume_results
|
|
165
|
-
|
|
167
|
+
mysql_args = {
|
|
168
|
+
"database": self.database_path,
|
|
169
|
+
"port": self.port,
|
|
170
|
+
"host": self.host,
|
|
171
|
+
"user": self.username,
|
|
172
|
+
"password": self.password,
|
|
173
|
+
"consume_results": True,
|
|
174
|
+
}
|
|
175
|
+
if self.charset:
|
|
176
|
+
mysql_args["charset"] = self.charset
|
|
177
|
+
self.dba = self.database_module.connect(**mysql_args)
|
|
166
178
|
elif self.database_engine == POSTGRES:
|
|
167
|
-
|
|
168
|
-
dbname
|
|
169
|
-
port
|
|
170
|
-
host
|
|
171
|
-
user
|
|
172
|
-
password
|
|
173
|
-
|
|
179
|
+
postgres_args = {
|
|
180
|
+
"dbname": self.database_path,
|
|
181
|
+
"port": self.port,
|
|
182
|
+
"host": self.host,
|
|
183
|
+
"user": self.username,
|
|
184
|
+
"password": self.password,
|
|
185
|
+
}
|
|
186
|
+
if self.charset:
|
|
187
|
+
postgres_args["client_encoding"] = self.charset
|
|
188
|
+
self.dba = self.database_module.connect(**postgres_args)
|
|
174
189
|
elif self.database_engine == MSSQL:
|
|
175
190
|
self.dba = self.database_module.connect(
|
|
176
191
|
server=self.host,
|
|
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-0.2.190 → tina4_python-0.2.191}/tina4_python/public/js/reconnecting-websocket.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/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
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.190 → tina4_python-0.2.191}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|