diff --git a/crudkit/config.py b/crudkit/config.py index ee3f3c1..a19d4d3 100644 --- a/crudkit/config.py +++ b/crudkit/config.py @@ -1,5 +1,6 @@ from __future__ import annotations import os +import os from typing import Dict, Any, Optional, Type from urllib.parse import quote_plus from pathlib import Path diff --git a/crudkit/core/base.py b/crudkit/core/base.py index c66aaf3..2204778 100644 --- a/crudkit/core/base.py +++ b/crudkit/core/base.py @@ -11,26 +11,37 @@ class CRUDMixin: def as_dict(self, fields: list[str] | None = None): """ - Serialize mapped columns. Honors projection if either: - - 'fields' is passed explicitly, or - - + Serialize the instance. + - If 'fields' (possibly dotted) is provided, emit exactly those keys. + - Else, if '__crudkit_projection__' is set on the instance, emit those keys. + - Else, fall back to all mapped columns on this class hierarchy. + Always includes 'id' when present unless explicitly excluded. """ - allowed = None + if fields is None: + fields = getattr(self, "__crudkit_projection__", None) + if fields: - allowed = set(fields) - else: - allowed = getattr(self, "__crudkit_root_fields__", None) + out = {} + if "id" not in fields and hasattr(self, "id"): + out["id"] = getattr(self, "id") + for f in fields: + cur = self + for part in f.split("."): + if cur is None: + break + cur = getattr(cur, part, None) + out[f] = cur + return out + result = {} - for cls in self.__class__.__mro__: - if not hasattr(cls, "__table__"): - continue - for column in cls.__table__.columns: - name = column.name - if allowed is not None and name not in allowed and name != "id": - continue - result[name] = getattr(self, name) + for cls in self.__clas__.__mro__: + if hasattr(cls, "__table__"): + for column in cls.__table__.columns: + name = column.name + result[name] = getattr(self, name) return result + class Version(Base): __tablename__ = "versions" diff --git a/crudkit/core/service.py b/crudkit/core/service.py index ab1be78..f84d276 100644 --- a/crudkit/core/service.py +++ b/crudkit/core/service.py @@ -101,23 +101,40 @@ class CRUDService(Generic[T]): if limit is not None and limit > 0: query = query.limit(limit) - # return query.all() rows = query.all() - try: - rf_names = [c.key for c in (root_fields or [])] - except NameError: - rf_names = [] - if rf_names: - allow = set(rf_names) - if "id" not in allow and hasattr(self.model, "id"): - allow.add("id") + proj = [] + if root_fields: + proj.extend(c.key for c in root_fields) + for path, names in (rel_field_names or {}).items(): + prefix = ".".join(path) + for n in names: + proj.append(f"{prefix}.{n}") + + if proj and "id" not in proj and hasattr(self.model, "id"): + proj.insert(0, "id") + + if proj: for obj in rows: try: - setattr(obj, "__crudkit_root_fields__", allow) + setattr(obj, "__crudkit_projection__", tuple(proj)) except Exception: pass + # try: + # rf_names = [c.key for c in (root_fields or [])] + # except NameError: + # rf_names = [] + # if rf_names: + # allow = set(rf_names) + # if "id" not in allow and hasattr(self.model, "id"): + # allow.add("id") + # for obj in rows: + # try: + # setattr(obj, "__crudkit_root_fields__", allow) + # except Exception: + # pass + return rows def create(self, data: dict, actor=None) -> T: diff --git a/crudkit/ui/fragments.py b/crudkit/ui/fragments.py index d30e24b..5913e65 100644 --- a/crudkit/ui/fragments.py +++ b/crudkit/ui/fragments.py @@ -35,7 +35,12 @@ def render_field(field, value): def render_table(objects): env = get_env() template = get_crudkit_template(env, 'table.html') - return template.render(objects=objects) + 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) def render_form(model_cls, values, session=None): env = get_env() diff --git a/crudkit/ui/templates/table.html b/crudkit/ui/templates/table.html index b4abd80..38ff48e 100644 --- a/crudkit/ui/templates/table.html +++ b/crudkit/ui/templates/table.html @@ -1,10 +1,10 @@
| {{ field.name }} | {% endfor %} + {% for field in fields if field != "id" %}{{ field }} | {% endfor %}
|---|---|
| {{ obj[field.name] }} | {% endfor %}|
| {{ cell }} | {% endfor %}|
| No data. |