Skip to content

Commit 4542f05

Browse files
Release v0.2.169
Updates to CRUD for automatically saving images.
1 parent 59d97b0 commit 4542f05

7 files changed

Lines changed: 110 additions & 20 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "tina4-python"
3-
version = "0.2.168"
3+
version = "0.2.169"
44
description = "Tina4Python - This is not another framework for Python"
55
authors = [
66
{name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}

‎src/__init__.py‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
import asyncio
22
from tina4_python.Migration import migrate
3-
from tina4_python import get, post, put, delete, middleware, description, Database, secured, HTTP_OK, noauth
3+
from tina4_python import get, post, put, delete, middleware, description, Database, secured, HTTP_OK, noauth, Debug, orm
4+
5+
6+
47

58
dba = Database("sqlite3:data.db")
69

710
migrate(dba)
11+
orm(dba)
12+
13+
# from .orm.User import User
14+
# user = User({"id": 1, "username": "john", "email": "john@doe.com"})
15+
# user.id = 1
16+
# user.username = "moo"
17+
# user.email = "test"
18+
#
19+
# if not dba.table_exists("user"):
20+
# Debug.info (user.create_table())
21+
# dba.execute(user.create_table())
22+
#
23+
# user.save()
24+
25+
26+
@get("/users/landing")
27+
async def get_users_landing(request, response):
28+
from .orm.User import User
29+
30+
crud_users = User().select("*").to_crud(request, {"card_view": True})
31+
32+
return response.render("{{crud_users}}", {"crud_users": crud_users})
33+
34+
835

936
@get("/system/employees")
1037
async def get_employees(request, response):

‎tina4_python/CRUD.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,21 @@ def to_crud(self, request, options=None):
252252

253253
twig_file = self.ensure_crud_template(crud_name + ".twig")
254254

255+
def parse_request_for_images(request):
256+
"""
257+
Parses request for images and files
258+
:param request:
259+
:return:
260+
"""
261+
# if there are files in the request the then I assign the file value to the request body
262+
if request.files:
263+
for input_name in request.files:
264+
prefix = ""
265+
if "image" in request.files[input_name]["content_type"]:
266+
prefix ="data:"+request.files[input_name]["content_type"]+";base64,"
267+
request.body[input_name] = prefix+request.files[input_name]["content"]
268+
return request
269+
255270
async def get_record(request, response):
256271
limit = int(request.params.get("limit", options.get("limit", 10)))
257272
offset = int(request.params.get("offset", options.get("offset", 0)))
@@ -285,12 +300,21 @@ async def get_record(request, response):
285300

286301
async def post_record(request, response):
287302
Debug.info("CRUD CREATE", table_name, request.body)
303+
304+
request = parse_request_for_images(request)
305+
288306
self.dba.insert(table_name, request.body, primary_key=options["primary_key"])
289307
self.dba.commit()
290308
return response({"message": f"<script>showMessage('{table_nice_name} Record added');</script>"}, HTTP_OK, APPLICATION_JSON)
291309

292310
async def update_record(request, response):
293311
Debug.info("CRUD UPDATE", table_name, request.params, request.body)
312+
# Add the primary key if it is taken out of the form
313+
if options["primary_key"] not in request.body:
314+
request.body[options["primary_key"]] = request.params[options["primary_key"]]
315+
316+
request = parse_request_for_images(request)
317+
294318
self.dba.update(table_name, request.body, primary_key=options["primary_key"])
295319
self.dba.commit()
296320
return response({"message": f"<script>showMessage('{table_nice_name} Record updated');</script>", "post": request.body}, HTTP_OK, APPLICATION_JSON)

‎tina4_python/FieldTypes.py‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import inspect
1010
from datetime import datetime
1111
from tina4_python.DatabaseTypes import MSSQL, POSTGRES, FIREBIRD
12+
from tina4_python import Debug
1213

1314
class BaseField:
1415
primary_key = False
@@ -83,15 +84,21 @@ def __init__(self, column_name=None, primary_key=False, default_value=None, auto
8384
self.decimal_places = decimal_places
8485

8586
if column_name is None:
86-
frame = inspect.stack()[1]
87-
# Parse python syntax of the assignment line
88-
st = ast.parse(frame.code_context[0].strip())
89-
stmt = st.body[0]
90-
# Assume class being instanced as simple assign statement
91-
assert (isinstance(stmt, ast.Assign))
92-
# Parse the target the class is assigned to
93-
target = stmt.targets[0]
94-
self.column_name = target.id
87+
try:
88+
frame = inspect.stack()[1]
89+
# Parse python syntax of the assignment line
90+
st = ast.parse(frame.code_context[0].strip())
91+
stmt = st.body[0]
92+
# Assume class being instanced as simple assign statement
93+
assert (isinstance(stmt, ast.Assign))
94+
# Parse the target the class is assigned to
95+
target = stmt.targets[0]
96+
if hasattr(target, 'id'):
97+
self.column_name = target.id
98+
else:
99+
raise Exception("Sorry we can't determine the column name for ORM ")
100+
except Exception as e:
101+
Debug.error("Error determining field column for ORM Object", str(e))
95102
else:
96103
self.column_name = column_name
97104

‎tina4_python/ORM.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
#
66
# flake8: noqa: E501
77
import base64
8-
from datetime import datetime, date
9-
import ast
8+
from datetime import date
109
import json
1110
import os
1211
from tina4_python.Constant import TINA4_LOG_ERROR
13-
from tina4_python.Debug import Debug
1412
from tina4_python.FieldTypes import *
1513

1614

@@ -186,7 +184,10 @@ def to_dict(self):
186184

187185
data[key] = current_value.value
188186
elif isinstance(value, IntegerField):
189-
data[key] = int(current_value)
187+
try:
188+
data[key] = int(current_value)
189+
except Exception as e:
190+
Debug.error("Could not save", current_value, "to", key)
190191
else:
191192
data[key] = str(current_value)
192193

‎tina4_python/__init__.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def start_in_thread(target, exception_hook=None):
217217
from .Testing import tests, assert_equal, assert_raises
218218
from .Debug import Debug
219219
from .Database import Database
220-
from .ORM import ORM
220+
from .ORM import ORM, orm
221221
from .Api import Api
222222
from .Template import template
223223
from .Swagger import description, secure, summary, example, example_response, tags, params, describe
@@ -231,10 +231,14 @@ def start_in_thread(target, exception_hook=None):
231231
if deco.__name__ not in builtins.__dict__:
232232
builtins.__dict__[deco.__name__] = deco
233233

234+
235+
234236
builtins.Debug = Debug
235237
builtins.Api = Api
236238
builtins.Database = Database
237239
builtins.ORM = ORM
240+
builtins.orm = orm
241+
238242

239243
# Auto-import everything from src folders
240244
if os.path.exists(root_path + os.sep + "src"):

‎tina4_python/templates/components/crud.twig‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
{# --- Only show image if it's base64-like and long enough --- #}
6060
{% if is_image and value|length > 50 and value is string %}
6161
{% set src = value %}
62-
{% if value[:10] != 'data:image/' %}
62+
{% if value[:11] != 'data:image/' %}
6363
{% set src = 'data:image/' ~ mime_type ~ ';base64,' ~ value %}
6464
{% endif %}
6565

@@ -131,7 +131,7 @@
131131
{# --- Only show image if it's base64-like and long enough --- #}
132132
{% if is_image %}
133133
{% set src = value %}
134-
{% if value[:10] != 'data:image/' %}
134+
{% if value[:11] != 'data:image/' %}
135135
{% set src = 'data:image/' ~ mime_type ~ ';base64,' ~ value %}
136136
{% endif %}
137137

@@ -253,7 +253,7 @@
253253
254254
html += `
255255
<div class="mb-3">
256-
<label class="form-label fw-bold">${label}</label>
256+
<label class="form-label">${label}</label>
257257
<div class="border rounded p-3 bg-light text-center">
258258
<img src="${imageSrc}"
259259
alt="Current ${label}"
@@ -317,7 +317,33 @@
317317
value = '';
318318
type = `{{ column.type|default('text') }}`;
319319
320-
html += `<div class="mb-3">
320+
if (field.indexOf('image') !== -1) {
321+
let imageSrc = value;
322+
if (value.length > 0 && !value.startsWith('data:image/')) {
323+
imageSrc = `data:image/${mimeType};base64,${value}`;
324+
}
325+
326+
html += `
327+
<div class="mb-3">
328+
<label class="form-label">${label}</label>
329+
<div class="border rounded p-3 bg-light text-center">
330+
<img src="${imageSrc}"
331+
alt="Current ${label}"
332+
class="img-thumbnail mb-3"
333+
style="max-height: 180px; max-width: 100%;">
334+
<div>
335+
<input type="file"
336+
accept="image/*"
337+
class="form-control form-control-sm"
338+
id="${field}"
339+
name="${field}">
340+
<small class="text-muted">Choose new image (optional)</small>
341+
</div>
342+
</div>
343+
</div>`;
344+
} else {
345+
346+
html += `<div class="mb-3">
321347
<label for="${field}" class="form-label">${label}</label>
322348
<input type="${type === 'password' ? 'password' : 'text'}"
323349
class="form-control"
@@ -326,6 +352,7 @@
326352
placeholder="${label}"
327353
value='${value}'>
328354
</div>`;
355+
}
329356
{% endfor %}
330357
331358
modalBody.innerHTML = html;

0 commit comments

Comments
 (0)