Add caching functions and enhance item retrieval in templates; implement dynamic field selection for list items
This commit is contained in:
parent
7e24aa0585
commit
09cfcee8b3
5 changed files with 110 additions and 7 deletions
|
|
@ -7,13 +7,16 @@ if TYPE_CHECKING:
|
|||
from .work_log import WorkLog
|
||||
from .rooms import Room
|
||||
from .image import Image
|
||||
from .users import User
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, Identity, Index, Integer, Unicode, DateTime, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
import datetime
|
||||
|
||||
from . import db
|
||||
from .brands import Brand
|
||||
from .image import ImageAttachable
|
||||
from .users import User
|
||||
|
||||
class Inventory(db.Model, ImageAttachable):
|
||||
__tablename__ = 'inventory'
|
||||
|
|
@ -131,3 +134,17 @@ class Inventory(db.Model, ImageAttachable):
|
|||
|
||||
def attach_image(self, image: Image) -> None:
|
||||
self.image = image
|
||||
|
||||
@staticmethod
|
||||
def ui_search(stmt, text: str):
|
||||
t = f"%{text}%"
|
||||
return stmt.where(
|
||||
Inventory.name.ilike(t) |
|
||||
Inventory.serial.ilike(t) |
|
||||
Inventory.model.ilike(t) |
|
||||
Inventory.notes.ilike(t) |
|
||||
Inventory.barcode.ilike(t) |
|
||||
Inventory.owner.has(User.first_name.ilike(t)) |
|
||||
Inventory.owner.has(User.last_name.ilike(t)) |
|
||||
Inventory.brand.has(Brand.name.ilike(t))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,57 @@
|
|||
from flask import Blueprint
|
||||
from flask import Blueprint, g
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
from . import inventory, user, worklog, settings, index, search, hooks
|
||||
from .. import db
|
||||
|
||||
def _cell_cache():
|
||||
if not hasattr(g, '_cell_cache'):
|
||||
g._cell_cache = {}
|
||||
return g._cell_cache
|
||||
|
||||
def _rows_cache():
|
||||
if not hasattr(g, '_rows_cache'):
|
||||
g._rows_cache = {}
|
||||
return g._rows_cache
|
||||
|
||||
@main.app_template_global()
|
||||
def cell(model_name: str, id_: int, field: str, default: str = ""):
|
||||
from ..ui.blueprint import get_model_class, call
|
||||
from ..ui.defaults import default_value
|
||||
key = (model_name, int(id_), field)
|
||||
cache = _cell_cache()
|
||||
if key in cache:
|
||||
return cache[key]
|
||||
|
||||
try:
|
||||
Model = get_model_class(model_name)
|
||||
val = call(Model, 'ui_value', db.session, id_=id_, field=field)
|
||||
if val is None:
|
||||
val = default_value(db.session, Model, id_=id_, field=field)
|
||||
except Exception:
|
||||
val = default
|
||||
if val is None:
|
||||
val = default
|
||||
cache[key] = val
|
||||
return val
|
||||
|
||||
@main.app_template_global()
|
||||
def cells(model_name: str, id_: int, *fields: str):
|
||||
from ..ui.blueprint import get_model_class, call
|
||||
from ..ui.defaults import default_values
|
||||
fields = [f for f in fields if f]
|
||||
key = (model_name, int(id_), tuple(fields))
|
||||
cache = _cell_cache()
|
||||
if key in cache:
|
||||
return cache[key]
|
||||
|
||||
try:
|
||||
Model = get_model_class(model_name)
|
||||
data = call(Model, 'ui_values', db.session, id_=int(id_), fields=fields)
|
||||
if data is None:
|
||||
data = default_values(db.session, Model, id_=int(id_), fields=fields)
|
||||
except Exception:
|
||||
data = {f: None for f in fields}
|
||||
cache[key] = data
|
||||
return data
|
||||
|
|
@ -11,4 +11,7 @@
|
|||
id = 'dropdown',
|
||||
refresh_url=url_for('ui.list_items', model_name='user')
|
||||
) }}
|
||||
|
||||
{% set vals = cells('user', 8, 'first_name', 'last_name') %}
|
||||
{{ vals['first_name'] }} {{ vals['last_name'] }}
|
||||
{% endblock %}
|
||||
|
|
@ -47,6 +47,9 @@ def call(Model: type, name: str, *args: Any, **kwargs: Any) -> Any:
|
|||
def list_items(model_name):
|
||||
Model = get_model_class(model_name)
|
||||
text = (request.args.get("q") or "").strip() or None
|
||||
fields_raw = (request.args.get("fields") or "").strip()
|
||||
fields = [f.strip() for f in fields_raw.split(",") if f.strip()]
|
||||
fields.extend(request.args.getlist("field"))
|
||||
|
||||
limit_param = request.args.get("limit")
|
||||
# 0 / -1 / blank => unlimited (pass 0)
|
||||
|
|
@ -91,6 +94,19 @@ def list_items(model_name):
|
|||
except TypeError:
|
||||
rows = [rows_any]
|
||||
|
||||
if fields:
|
||||
items = []
|
||||
for r in rows:
|
||||
row = {"id": r.id}
|
||||
for f in fields:
|
||||
if '.' in f:
|
||||
rel, attr = f.split('.', 1)
|
||||
rel_obj = getattr(r, rel, None)
|
||||
row[f] = getattr(rel_obj, attr, None) if rel_obj else None
|
||||
else:
|
||||
row[f] = getattr(r, f, None)
|
||||
items.append(row)
|
||||
else:
|
||||
items = [
|
||||
(call(Model, "ui_serialize", r, view=view) or default_serialize(Model, r, view=view))
|
||||
for r in rows
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
from sqlalchemy import select, asc as sa_asc, desc as sa_desc
|
||||
from sqlalchemy import select, asc as sa_asc, desc as sa_desc, or_
|
||||
from sqlalchemy.inspection import inspect
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy.sql import Select
|
||||
from sqlalchemy.sql.sqltypes import String, Unicode, Text
|
||||
from typing import Any, Optional, cast, Iterable
|
||||
|
||||
PREFERRED_LABELS = ("identifier", "name", "first_name", "last_name", "description")
|
||||
|
||||
def _columns_for_text_search(Model):
|
||||
mapper = inspect(Model)
|
||||
cols = []
|
||||
for c in mapper.columns:
|
||||
if isinstance(c.type, (String, Unicode, Text)):
|
||||
cols.append(getattr(Model, c.key))
|
||||
|
||||
return cols
|
||||
|
||||
def _mapped_column(Model, attr):
|
||||
"""Return the mapped column attr on the class (InstrumentedAttribute) or None"""
|
||||
mapper = inspect(Model)
|
||||
|
|
@ -51,6 +61,11 @@ def default_query(
|
|||
ui_search = getattr(Model, "ui_search", None)
|
||||
if callable(ui_search) and text:
|
||||
stmt = cast(Select[Any], ui_search(stmt, text))
|
||||
elif text:
|
||||
t = f"%{text}%"
|
||||
text_cols = _columns_for_text_search(Model)
|
||||
if text_cols:
|
||||
stmt = stmt.where(or_(*(col.ilike(t) for col in text_cols)))
|
||||
|
||||
if sort:
|
||||
ui_sort = getattr(Model, "ui_sort", None)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue