diff --git a/crudkit/dsl.py b/crudkit/dsl.py
index a9ee931..5a4e5b8 100644
--- a/crudkit/dsl.py
+++ b/crudkit/dsl.py
@@ -108,6 +108,11 @@ def build_query(Model, spec: QuerySpec, eager_policy=None):
col = getattr(Model, key[1:] if desc_ else key)
stmt = stmt.order_by(desc(col) if desc_ else asc(col))
+ if not spec.order_by and spec.page and spec.per_page:
+ pk_cols = inspect(Model).primary_key
+ if pk_cols:
+ stmt = stmt.order_by(*(asc(c) for c in pk_cols))
+
# eager loading
if eager_policy:
opts = eager_policy(Model, spec.expand)
diff --git a/crudkit/html/templates/crudkit/form.html b/crudkit/html/templates/crudkit/form.html
index 5f3bfb3..761658a 100644
--- a/crudkit/html/templates/crudkit/form.html
+++ b/crudkit/html/templates/crudkit/form.html
@@ -1,3 +1,3 @@
-{% import "_macros.html" as ui %}
+{% import "crudkit/_macros.html" as ui %}
{% set action = url_for('frags.save', model=model) %}
{{ ui.form(schema, action, method="POST", obj_id=obj.id if obj else None, hx=true) }}
\ No newline at end of file
diff --git a/crudkit/html/templates/crudkit/lis.html b/crudkit/html/templates/crudkit/lis.html
index e9b1813..3ce62e7 100644
--- a/crudkit/html/templates/crudkit/lis.html
+++ b/crudkit/html/templates/crudkit/lis.html
@@ -1,2 +1,2 @@
-{% import "_macros.html" as ui %}
+{% import "crudkit/_macros.html" as ui %}
{{ ui.lis(items, label_path=label_path, sublabel_path=sublabel_path, getp=getp) }}
diff --git a/crudkit/html/templates/crudkit/options.html b/crudkit/html/templates/crudkit/options.html
index 34d6a2b..0f66d2d 100644
--- a/crudkit/html/templates/crudkit/options.html
+++ b/crudkit/html/templates/crudkit/options.html
@@ -1,3 +1,3 @@
{# Renders only rows #}
-{% import "_macros.html" as ui %}
+{% import "crudkit/_macros.html" as ui %}
{{ ui.options(items, value_attr=value_attr, label_path=label_path, getp=getp) }}
diff --git a/crudkit/html/templates/crudkit/row.html b/crudkit/html/templates/crudkit/row.html
index a3ac629..719c0ba 100644
--- a/crudkit/html/templates/crudkit/row.html
+++ b/crudkit/html/templates/crudkit/row.html
@@ -1,2 +1,2 @@
-{% import "_macros.html" as ui %}
+{% import "crudkit/_macros.html" as ui %}
{{ ui.rows([obj], fields, getp=getp) }}
\ No newline at end of file
diff --git a/crudkit/html/templates/crudkit/rows.html b/crudkit/html/templates/crudkit/rows.html
index 7fafa3a..1c740f1 100644
--- a/crudkit/html/templates/crudkit/rows.html
+++ b/crudkit/html/templates/crudkit/rows.html
@@ -1,3 +1,3 @@
-{% import "_macros.html" as ui %}
+{% import "crudkit/_macros.html" as ui %}
{{ ui.rows(items, fields, getp=getp) }}
{{ ui.pager(model, page, pages, per_page, sort, filters) }}
diff --git a/crudkit/html/ui_fragments.py b/crudkit/html/ui_fragments.py
index 525c6e5..03182a7 100644
--- a/crudkit/html/ui_fragments.py
+++ b/crudkit/html/ui_fragments.py
@@ -20,7 +20,7 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
GET //frag/rows -> ...
+ pager markup if wanted
GET //frag/form -> (auto-generated)
"""
- bp = Blueprint(name, __name__, template_folder="templates/crudkit")
+ bp = Blueprint(name, __name__, template_folder="templates")
def session(): return scoped_session(db_session_factory)()
def _parse_filters(args):
@@ -74,7 +74,7 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
s = session(); svc = CrudService(s, default_eager_policy)
items, _ = svc.list(Model, spec)
- return render_template("options.html", items=items, value_attr=value_attr, label_path=label_path, getp=_getp)
+ return render_template("crudkit/options.html", items=items, value_attr=value_attr, label_path=label_path, getp=_getp)
@bp.get("//frag/lis")
def lis(model):
@@ -91,7 +91,7 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
s = session(); svc = CrudService(s, default_eager_policy)
rows, total = svc.list(Model, spec)
pages = (ceil(total / per_page) if page and per_page else 1)
- return render_template("lis.html", items=rows, label_path=label_path, sublabel_path=sublabel_path, page=page or 1, per_page=per_page or 1, total=total, model=model, sort=sort, filters=filters, getp=_getp)
+ return render_template("crudkit/lis.html", items=rows, label_path=label_path, sublabel_path=sublabel_path, page=page or 1, per_page=per_page or 1, total=total, model=model, sort=sort, filters=filters, getp=_getp)
@bp.get("//frag/rows")
def rows(model):
@@ -108,7 +108,7 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
s = session(); svc = CrudService(s, default_eager_policy)
rows, total = svc.list(Model, spec)
pages = max(1, ceil(total / per_page))
- return render_template("rows.html", items=rows, fields=fields, page=page, pages=pages, per_page=per_page, total=total, model=model, sort=sort, filters=filters, getp=_getp)
+ return render_template("crudkit/rows.html", items=rows, fields=fields, page=page, pages=pages, per_page=per_page, total=total, model=model, sort=sort, filters=filters, getp=_getp)
@bp.get("//frag/form")
def form(model):
@@ -123,7 +123,7 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
schema = build_form_schema(Model, s, obj=obj, include=include)
hx = request.args.get("hx", type=int) == 1
- return render_template("form.html", model=model, obj=obj, schema=schema, hx=hx)
+ return render_template("crudkit/form.html", model=model, obj=obj, schema=schema, hx=hx)
def coerce_form_types(Model, data: dict) -> dict:
"""Turn HTML string inputs into the Python types your columns expect."""