Redesign1 #1
5 changed files with 64 additions and 30 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
|
import os
|
||||||
from typing import Dict, Any, Optional, Type
|
from typing import Dict, Any, Optional, Type
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,37 @@ class CRUDMixin:
|
||||||
|
|
||||||
def as_dict(self, fields: list[str] | None = None):
|
def as_dict(self, fields: list[str] | None = None):
|
||||||
"""
|
"""
|
||||||
Serialize mapped columns. Honors projection if either:
|
Serialize the instance.
|
||||||
- 'fields' is passed explicitly, or
|
- 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:
|
if fields:
|
||||||
allowed = set(fields)
|
out = {}
|
||||||
else:
|
if "id" not in fields and hasattr(self, "id"):
|
||||||
allowed = getattr(self, "__crudkit_root_fields__", None)
|
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 = {}
|
result = {}
|
||||||
for cls in self.__class__.__mro__:
|
for cls in self.__clas__.__mro__:
|
||||||
if not hasattr(cls, "__table__"):
|
if hasattr(cls, "__table__"):
|
||||||
continue
|
for column in cls.__table__.columns:
|
||||||
for column in cls.__table__.columns:
|
name = column.name
|
||||||
name = column.name
|
result[name] = getattr(self, name)
|
||||||
if allowed is not None and name not in allowed and name != "id":
|
|
||||||
continue
|
|
||||||
result[name] = getattr(self, name)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Version(Base):
|
class Version(Base):
|
||||||
__tablename__ = "versions"
|
__tablename__ = "versions"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,23 +101,40 @@ class CRUDService(Generic[T]):
|
||||||
if limit is not None and limit > 0:
|
if limit is not None and limit > 0:
|
||||||
query = query.limit(limit)
|
query = query.limit(limit)
|
||||||
|
|
||||||
# return query.all()
|
|
||||||
rows = query.all()
|
rows = query.all()
|
||||||
|
|
||||||
try:
|
proj = []
|
||||||
rf_names = [c.key for c in (root_fields or [])]
|
if root_fields:
|
||||||
except NameError:
|
proj.extend(c.key for c in root_fields)
|
||||||
rf_names = []
|
for path, names in (rel_field_names or {}).items():
|
||||||
if rf_names:
|
prefix = ".".join(path)
|
||||||
allow = set(rf_names)
|
for n in names:
|
||||||
if "id" not in allow and hasattr(self.model, "id"):
|
proj.append(f"{prefix}.{n}")
|
||||||
allow.add("id")
|
|
||||||
|
if proj and "id" not in proj and hasattr(self.model, "id"):
|
||||||
|
proj.insert(0, "id")
|
||||||
|
|
||||||
|
if proj:
|
||||||
for obj in rows:
|
for obj in rows:
|
||||||
try:
|
try:
|
||||||
setattr(obj, "__crudkit_root_fields__", allow)
|
setattr(obj, "__crudkit_projection__", tuple(proj))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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
|
return rows
|
||||||
|
|
||||||
def create(self, data: dict, actor=None) -> T:
|
def create(self, data: dict, actor=None) -> T:
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,12 @@ def render_field(field, value):
|
||||||
def render_table(objects):
|
def render_table(objects):
|
||||||
env = get_env()
|
env = get_env()
|
||||||
template = get_crudkit_template(env, 'table.html')
|
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):
|
def render_form(model_cls, values, session=None):
|
||||||
env = get_env()
|
env = get_env()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<table>
|
<table>
|
||||||
{% if objects %}
|
{% if rows %}
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
{% for obj in objects %}
|
{% for row in rows %}
|
||||||
<tr>{% for field in obj.__table__.columns %}<td>{{ obj[field.name] }}</td>{% endfor %}</tr>
|
<tr>{% for _, cell in row.items() if _ != "id" %}<td>{{ cell }}</td>{% endfor %}</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><th>No data.</th></tr>
|
<tr><th>No data.</th></tr>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue