diff --git a/CHANGELOG.md b/CHANGELOG.md index 3508534..bec810a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,6 @@ 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.13.3 (2024-08-30) - -### Fix - -- move model base class out of model subpkg - ## v0.13.2 (2024-08-27) ### Fix diff --git a/docs/api/wuttjamaican/db.util.rst b/docs/api/wuttjamaican/db.util.rst deleted file mode 100644 index 66b4df1..0000000 --- a/docs/api/wuttjamaican/db.util.rst +++ /dev/null @@ -1,6 +0,0 @@ - -``wuttjamaican.db.util`` -======================== - -.. automodule:: wuttjamaican.db.util - :members: diff --git a/docs/api/wuttjamaican/index.rst b/docs/api/wuttjamaican/index.rst index 0c9d7c0..69a754e 100644 --- a/docs/api/wuttjamaican/index.rst +++ b/docs/api/wuttjamaican/index.rst @@ -17,7 +17,6 @@ db.model.base db.model.upgrades db.sess - db.util email email.handler email.message diff --git a/pyproject.toml b/pyproject.toml index a970992..f82673d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttJamaican" -version = "0.13.3" +version = "0.13.2" description = "Base package for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] diff --git a/src/wuttjamaican/db/model/__init__.py b/src/wuttjamaican/db/model/__init__.py index d623775..267738c 100644 --- a/src/wuttjamaican/db/model/__init__.py +++ b/src/wuttjamaican/db/model/__init__.py @@ -27,6 +27,8 @@ This is the default :term:`app model` module. The ``wuttjamaican.db.model`` namespace contains the following: +* :func:`~wuttjamaican.db.model.base.uuid_column()` +* :func:`~wuttjamaican.db.model.base.uuid_fk_column()` * :class:`~wuttjamaican.db.model.base.Base` * :class:`~wuttjamaican.db.model.base.Setting` * :class:`~wuttjamaican.db.model.base.Person` @@ -37,9 +39,6 @@ The ``wuttjamaican.db.model`` namespace contains the following: * :class:`~wuttjamaican.db.model.upgrades.Upgrade` """ -# TODO: remove these -from wuttjamaican.db.util import uuid_column, uuid_fk_column - -from .base import Base, Setting, Person +from .base import uuid_column, uuid_fk_column, Base, Setting, Person from .auth import Role, Permission, User, UserRole from .upgrades import Upgrade diff --git a/src/wuttjamaican/db/model/base.py b/src/wuttjamaican/db/model/base.py index 24c850b..6da0cb3 100644 --- a/src/wuttjamaican/db/model/base.py +++ b/src/wuttjamaican/db/model/base.py @@ -31,15 +31,62 @@ Base Models import sqlalchemy as sa from sqlalchemy import orm -from wuttjamaican.db.util import (naming_convention, ModelBase, - uuid_column, uuid_fk_column) +from wuttjamaican.util import make_uuid +# nb. this convention comes from upstream docs +# https://docs.sqlalchemy.org/en/14/core/constraints.html#constraint-naming-conventions +naming_convention = { + 'ix': 'ix_%(column_0_label)s', + 'uq': 'uq_%(table_name)s_%(column_0_name)s', + 'ck': 'ck_%(table_name)s_%(constraint_name)s', + 'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s', + 'pk': 'pk_%(table_name)s', +} + metadata = sa.MetaData(naming_convention=naming_convention) + +class ModelBase: + """ """ + + def __iter__(self): + # nb. we override this to allow for `dict(self)` + state = sa.inspect(self) + fields = [attr.key for attr in state.attrs] + return iter([(field, getattr(self, field)) + for field in fields]) + + def __getitem__(self, key): + # nb. we override this to allow for `x = self['field']` + state = sa.inspect(self) + if hasattr(state.attrs, key): + return getattr(self, key) + + Base = orm.declarative_base(metadata=metadata, cls=ModelBase) +def uuid_column(*args, **kwargs): + """ + Returns a UUID column for use as a table's primary key. + """ + kwargs.setdefault('primary_key', True) + kwargs.setdefault('nullable', False) + kwargs.setdefault('default', make_uuid) + return sa.Column(sa.String(length=32), *args, **kwargs) + + +def uuid_fk_column(target_column, *args, **kwargs): + """ + Returns a UUID column for use as a foreign key to another table. + + :param target_column: Name of the table column on the remote side, + e.g. ``'user.uuid'``. + """ + return sa.Column(sa.String(length=32), sa.ForeignKey(target_column), *args, **kwargs) + + class Setting(Base): """ Represents a :term:`config setting`. diff --git a/src/wuttjamaican/db/util.py b/src/wuttjamaican/db/util.py deleted file mode 100644 index 88362c4..0000000 --- a/src/wuttjamaican/db/util.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8; -*- -################################################################################ -# -# WuttJamaican -- Base package for Wutta Framework -# Copyright © 2023-2024 Lance Edgar -# -# This file is part of Wutta Framework. -# -# Wutta Framework is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# Wutta Framework is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# Wutta Framework. If not, see . -# -################################################################################ -""" -Database Utilities -""" - -import sqlalchemy as sa - -from wuttjamaican.util import make_uuid - - -# nb. this convention comes from upstream docs -# https://docs.sqlalchemy.org/en/14/core/constraints.html#constraint-naming-conventions -naming_convention = { - 'ix': 'ix_%(column_0_label)s', - 'uq': 'uq_%(table_name)s_%(column_0_name)s', - 'ck': 'ck_%(table_name)s_%(constraint_name)s', - 'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s', - 'pk': 'pk_%(table_name)s', -} - - -class ModelBase: - """ """ - - def __iter__(self): - # nb. we override this to allow for `dict(self)` - state = sa.inspect(self) - fields = [attr.key for attr in state.attrs] - return iter([(field, getattr(self, field)) - for field in fields]) - - def __getitem__(self, key): - # nb. we override this to allow for `x = self['field']` - state = sa.inspect(self) - if hasattr(state.attrs, key): - return getattr(self, key) - - -def uuid_column(*args, **kwargs): - """ - Returns a UUID column for use as a table's primary key. - """ - kwargs.setdefault('primary_key', True) - kwargs.setdefault('nullable', False) - kwargs.setdefault('default', make_uuid) - return sa.Column(sa.String(length=32), *args, **kwargs) - - -def uuid_fk_column(target_column, *args, **kwargs): - """ - Returns a UUID column for use as a foreign key to another table. - - :param target_column: Name of the table column on the remote side, - e.g. ``'user.uuid'``. - """ - return sa.Column(sa.String(length=32), sa.ForeignKey(target_column), *args, **kwargs) diff --git a/tests/db/model/test_base.py b/tests/db/model/test_base.py index 50e95a5..1d55436 100644 --- a/tests/db/model/test_base.py +++ b/tests/db/model/test_base.py @@ -3,16 +3,42 @@ from unittest import TestCase try: - from wuttjamaican.db.model import base as mod + import sqlalchemy as sa + from wuttjamaican.db.model import base as model from wuttjamaican.db.model.auth import User except ImportError: pass else: + class TestModelBase(TestCase): + + def test_dict_behavior(self): + setting = model.Setting() + self.assertEqual(list(iter(setting)), [('name', None), ('value', None)]) + self.assertIsNone(setting['name']) + setting.name = 'foo' + self.assertEqual(setting['name'], 'foo') + + class TestUUIDColumn(TestCase): + + def test_basic(self): + column = model.uuid_column() + self.assertIsInstance(column, sa.Column) + self.assertIsInstance(column.type, sa.String) + self.assertEqual(column.type.length, 32) + + class TestUUIDFKColumn(TestCase): + + def test_basic(self): + column = model.uuid_fk_column('foo.bar') + self.assertIsInstance(column, sa.Column) + self.assertIsInstance(column.type, sa.String) + self.assertEqual(column.type.length, 32) + class TestSetting(TestCase): def test_basic(self): - setting = mod.Setting() + setting = model.Setting() self.assertEqual(str(setting), "") setting.name = 'foo' self.assertEqual(str(setting), "foo") @@ -20,13 +46,13 @@ else: class TestPerson(TestCase): def test_basic(self): - person = mod.Person() + person = model.Person() self.assertEqual(str(person), "") person.full_name = "Barney Rubble" self.assertEqual(str(person), "Barney Rubble") def test_users(self): - person = mod.Person() + person = model.Person() self.assertIsNone(person.user) user = User() diff --git a/tests/db/test_util.py b/tests/db/test_util.py deleted file mode 100644 index 566b68a..0000000 --- a/tests/db/test_util.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8; -*- - -from unittest import TestCase - - -try: - import sqlalchemy as sa - from wuttjamaican.db import util as mod - from wuttjamaican.db.model.base import Setting -except ImportError: - pass -else: - - - class TestModelBase(TestCase): - - def test_dict_behavior(self): - setting = Setting() - self.assertEqual(list(iter(setting)), [('name', None), ('value', None)]) - self.assertIsNone(setting['name']) - setting.name = 'foo' - self.assertEqual(setting['name'], 'foo') - - - class TestUUIDColumn(TestCase): - - def test_basic(self): - column = mod.uuid_column() - self.assertIsInstance(column, sa.Column) - self.assertIsInstance(column.type, sa.String) - self.assertEqual(column.type.length, 32) - - - class TestUUIDFKColumn(TestCase): - - def test_basic(self): - column = mod.uuid_fk_column('foo.bar') - self.assertIsInstance(column, sa.Column) - self.assertIsInstance(column.type, sa.String) - self.assertEqual(column.type.length, 32)