diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee32008..8c4f50d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to WuttJamaican will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## v0.16.1 (2024-12-05)
+
+### Fix
+
+- add `db.util.make_topo_sortkey()` function
+- use true UUID type for Upgrades table primary key
+- let caller set data type for `uuid_column()` and `uuid_fk_column()`
+- avoid error when loading installer templates
+
## v0.16.0 (2024-11-30)
### Feat
diff --git a/docs/narr/install/quickstart.rst b/docs/narr/install/quickstart.rst
index 0fea804..84a491d 100644
--- a/docs/narr/install/quickstart.rst
+++ b/docs/narr/install/quickstart.rst
@@ -22,35 +22,39 @@ Make a parent folder for all source code:
mkdir -p ~/src
-Make a :term:`virtual environment` for your project:
+Make and activate a new :term:`virtual environment` for your project:
.. code-block:: sh
cd /path/to/envs
python3 -m venv poser
+ source poser/bin/activate
Make a new e.g. ``poser`` database in PostgreSQL (or MySQL).
-Install and run cookiecutter with wuttaweb template:
+Install and run `cookiecutter `_
+with `wuttaweb template
+`_:
.. code-block:: sh
- cd /path/to/envs/poser
- bin/pip install cookiecutter
- bin/cookiecutter -o ~/src git+https://forgejo.wuttaproject.org/wutta/cookiecutter-wuttaweb
+ pip install cookiecutter
+ cookiecutter -o ~/src git+https://forgejo.wuttaproject.org/wutta/cookiecutter-wuttaweb
Assuming you now have project code at ``~/src/poser`` then install
-that and run the app installer:
+that and run the app installer. Note the 2nd command name will depend
+on your project:
.. code-block:: sh
- bin/pip install -e ~/src/poser
- bin/poser install
+ pip install -e ~/src/poser
+ poser install
If all goes well, you can run the web app with:
.. code-block:: sh
+ cd /path/to/envs/poser
bin/pserve --reload file+ini:app/web.conf
And browse it at http://localhost:9080
diff --git a/pyproject.toml b/pyproject.toml
index 96ce552..d9c0ce8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,10 +6,10 @@ build-backend = "hatchling.build"
[project]
name = "WuttJamaican"
-version = "0.16.0"
+version = "0.16.1"
description = "Base package for Wutta Framework"
readme = "README.md"
-authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
+authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
license = {text = "GNU GPL v3+"}
classifiers = [
"Development Status :: 4 - Beta",
@@ -49,6 +49,7 @@ wutta = "wuttjamaican.cli:wutta_typer"
[project.urls]
Homepage = "https://wuttaproject.org/"
Repository = "https://forgejo.wuttaproject.org/wutta/wuttjamaican"
+Issues = "https://forgejo.wuttaproject.org/wutta/wuttjamaican/issues"
Changelog = "https://forgejo.wuttaproject.org/wutta/wuttjamaican/src/branch/master/CHANGELOG.md"
diff --git a/src/wuttjamaican/db/util.py b/src/wuttjamaican/db/util.py
index 0df0e68..643e267 100644
--- a/src/wuttjamaican/db/util.py
+++ b/src/wuttjamaican/db/util.py
@@ -27,6 +27,7 @@ Database Utilities
import uuid as _uuid
import sqlalchemy as sa
+from sqlalchemy import orm
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from wuttjamaican.util import make_uuid
@@ -127,3 +128,26 @@ def uuid_fk_column(target_column, *args, **kwargs):
if not args:
args = (sa.String(length=32), sa.ForeignKey(target_column))
return sa.Column(*args, **kwargs)
+
+
+def make_topo_sortkey(model):
+ """
+ Returns a function suitable for use as a ``key`` kwarg to a
+ standard Python sorting call. This key function will expect a
+ single class mapper and return a sequence number associated with
+ that model. The sequence is determined by SQLAlchemy's
+ topological table sorting.
+
+ :param model: Usually the :term:`app model`, but can be any module
+ containing model classes.
+ """
+ metadata = model.Base.metadata
+ tables = dict([(table.name, i)
+ for i, table in enumerate(metadata.sorted_tables, 1)])
+
+ def sortkey(name):
+ cls = getattr(model, name)
+ mapper = orm.class_mapper(cls)
+ return tuple(tables[t.name] for t in mapper.tables)
+
+ return sortkey
diff --git a/tests/db/test_util.py b/tests/db/test_util.py
index 2f0c9ed..8ffef76 100644
--- a/tests/db/test_util.py
+++ b/tests/db/test_util.py
@@ -11,6 +11,7 @@ try:
from wuttjamaican.db import util as mod
from wuttjamaican.db.model.base import Setting
from wuttjamaican.util import make_true_uuid
+ from wuttjamaican.testing import DataTestCase
except ImportError:
pass
else:
@@ -124,3 +125,17 @@ else:
self.assertIsInstance(column, sa.Column)
self.assertIsInstance(column.type, sa.String)
self.assertEqual(column.type.length, 32)
+
+
+ class TestMakeTopoSortkey(DataTestCase):
+
+ def test_basic(self):
+ model = self.app.model
+ sortkey = mod.make_topo_sortkey(model)
+ original = ['User', 'Person', 'UserRole', 'Role']
+
+ # models are sorted so dependants come later
+ result = sorted(original, key=sortkey)
+ self.assertTrue(result.index('Role') < result.index('UserRole'))
+ self.assertTrue(result.index('User') < result.index('UserRole'))
+ self.assertTrue(result.index('Person') < result.index('User'))