inventory/crudkit/ui/fragments.py
2025-09-10 09:18:31 -05:00

88 lines
2.8 KiB
Python

import os
from flask import current_app
from jinja2 import Environment, FileSystemLoader, ChoiceLoader
from sqlalchemy.orm import class_mapper, RelationshipProperty
from typing import List
def get_env():
app_loader = current_app.jinja_loader
default_path = os.path.join(os.path.dirname(__file__), 'templates')
fallback_loader = FileSystemLoader(default_path)
env = Environment(loader=ChoiceLoader([
app_loader,
fallback_loader
]))
return env
def get_crudkit_template(env, name):
try:
return env.get_template(f'crudkit/{name}')
except Exception:
return env.get_template(name)
def render_field(field, value):
env = get_env()
template = get_crudkit_template(env, 'field.html')
return template.render(
field_name=field['name'],
field_label=field.get('label', field['name']),
value=value,
field_type=field.get('type', 'text'),
options=field.get('options', None)
)
def render_table(objects, headers: List[str] | None = None):
env = get_env()
template = get_crudkit_template(env, 'table.html')
if not objects:
return template.render(fields=[], rows=[])
proj = getattr(objects[0], "__crudkit_projection__", None)
rows = [obj.as_dict(proj) for obj in objects]
fields = list(rows[0].keys())
return template.render(fields=fields, rows=rows, headers=headers)
def render_form(model_cls, values, session=None):
env = get_env()
template = get_crudkit_template(env, 'form.html')
fields = []
fk_fields = set()
mapper = class_mapper(model_cls)
for prop in mapper.iterate_properties:
# FK Relationship fields (many-to-one)
if isinstance(prop, RelationshipProperty) and prop.direction.name == 'MANYTOONE':
if session is None:
continue
related_model = prop.mapper.class_
options = session.query(related_model).all()
fields.append({
'name': f"{prop.key}_id",
'label': prop.key,
'type': 'select',
'options': [
{'value': getattr(obj, 'id'), 'label': str(obj)}
for obj in options
]
})
fk_fields.add(f"{prop.key}_id")
# Now add basic columns — excluding FKs already covered
for col in model_cls.__table__.columns:
if col.name in fk_fields:
continue
if col.name in ('id', 'created_at', 'updated_at'):
continue
if col.default or col.server_default or col.onupdate:
continue
fields.append({
'name': col.name,
'label': col.name,
'type': 'text',
})
return template.render(fields=fields, values=values, render_field=render_field)