Fixing projections to include requested relationship fields.
This commit is contained in:
parent
e09cee0c79
commit
82062d72d6
5 changed files with 64 additions and 30 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 cls in self.__clas__.__mro__:
|
||||
if hasattr(cls, "__table__"):
|
||||
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)
|
||||
return result
|
||||
|
||||
|
||||
class Version(Base):
|
||||
__tablename__ = "versions"
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<table>
|
||||
{% if objects %}
|
||||
{% if rows %}
|
||||
<tr>
|
||||
{% for field in objects[0].__table__.columns %}<th>{{ field.name }}</th>{% endfor %}
|
||||
{% for field in fields if field != "id" %}<th>{{ field }}</th>{% endfor %}
|
||||
</tr>
|
||||
{% for obj in objects %}
|
||||
<tr>{% for field in obj.__table__.columns %}<td>{{ obj[field.name] }}</td>{% endfor %}</tr>
|
||||
{% for row in rows %}
|
||||
<tr>{% for _, cell in row.items() if _ != "id" %}<td>{{ cell }}</td>{% endfor %}</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><th>No data.</th></tr>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue