from jinja2 import Environment, FileSystemLoader, ChoiceLoader from sqlalchemy.orm import class_mapper, RelationshipProperty from flask import current_app import os 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): env = get_env() template = get_crudkit_template(env, 'table.html') return template.render(objects=objects) 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)