From 4c51189d41f5d962bceb214da850ab657ea00782 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 30 Aug 2024 20:30:09 -0500 Subject: [PATCH 1/2] fix: move model base class out of model subpkg and other things, used by other packages (rattail) otherwise when rattail imports them, the whole WJ model comes along with it and it can interfere with sqlalchemy-continuum versioning --- docs/api/wuttjamaican/db.util.rst | 6 +++ docs/api/wuttjamaican/index.rst | 1 + src/wuttjamaican/db/model/__init__.py | 7 +-- src/wuttjamaican/db/model/base.py | 51 +----------------- src/wuttjamaican/db/util.py | 77 +++++++++++++++++++++++++++ tests/db/model/test_base.py | 34 ++---------- tests/db/test_util.py | 40 ++++++++++++++ 7 files changed, 134 insertions(+), 82 deletions(-) create mode 100644 docs/api/wuttjamaican/db.util.rst create mode 100644 src/wuttjamaican/db/util.py create mode 100644 tests/db/test_util.py diff --git a/docs/api/wuttjamaican/db.util.rst b/docs/api/wuttjamaican/db.util.rst new file mode 100644 index 0000000..66b4df1 --- /dev/null +++ b/docs/api/wuttjamaican/db.util.rst @@ -0,0 +1,6 @@ + +``wuttjamaican.db.util`` +======================== + +.. automodule:: wuttjamaican.db.util + :members: diff --git a/docs/api/wuttjamaican/index.rst b/docs/api/wuttjamaican/index.rst index 69a754e..0c9d7c0 100644 --- a/docs/api/wuttjamaican/index.rst +++ b/docs/api/wuttjamaican/index.rst @@ -17,6 +17,7 @@ db.model.base db.model.upgrades db.sess + db.util email email.handler email.message diff --git a/src/wuttjamaican/db/model/__init__.py b/src/wuttjamaican/db/model/__init__.py index 267738c..d623775 100644 --- a/src/wuttjamaican/db/model/__init__.py +++ b/src/wuttjamaican/db/model/__init__.py @@ -27,8 +27,6 @@ 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` @@ -39,6 +37,9 @@ The ``wuttjamaican.db.model`` namespace contains the following: * :class:`~wuttjamaican.db.model.upgrades.Upgrade` """ -from .base import uuid_column, uuid_fk_column, Base, Setting, Person +# TODO: remove these +from wuttjamaican.db.util import uuid_column, uuid_fk_column + +from .base import 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 6da0cb3..24c850b 100644 --- a/src/wuttjamaican/db/model/base.py +++ b/src/wuttjamaican/db/model/base.py @@ -31,62 +31,15 @@ Base Models import sqlalchemy as sa from sqlalchemy import orm -from wuttjamaican.util import make_uuid +from wuttjamaican.db.util import (naming_convention, ModelBase, + uuid_column, uuid_fk_column) -# 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 new file mode 100644 index 0000000..88362c4 --- /dev/null +++ b/src/wuttjamaican/db/util.py @@ -0,0 +1,77 @@ +# -*- 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 1d55436..50e95a5 100644 --- a/tests/db/model/test_base.py +++ b/tests/db/model/test_base.py @@ -3,42 +3,16 @@ from unittest import TestCase try: - import sqlalchemy as sa - from wuttjamaican.db.model import base as model + from wuttjamaican.db.model import base as mod 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 = model.Setting() + setting = mod.Setting() self.assertEqual(str(setting), "") setting.name = 'foo' self.assertEqual(str(setting), "foo") @@ -46,13 +20,13 @@ else: class TestPerson(TestCase): def test_basic(self): - person = model.Person() + person = mod.Person() self.assertEqual(str(person), "") person.full_name = "Barney Rubble" self.assertEqual(str(person), "Barney Rubble") def test_users(self): - person = model.Person() + person = mod.Person() self.assertIsNone(person.user) user = User() diff --git a/tests/db/test_util.py b/tests/db/test_util.py new file mode 100644 index 0000000..566b68a --- /dev/null +++ b/tests/db/test_util.py @@ -0,0 +1,40 @@ +# -*- 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) From cb147c203db200136010cd1d96b027fa48ea3d97 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 30 Aug 2024 20:34:19 -0500 Subject: [PATCH 2/2] =?UTF-8?q?bump:=20version=200.13.2=20=E2=86=92=200.13?= =?UTF-8?q?.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bec810a..3508534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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/pyproject.toml b/pyproject.toml index f82673d..a970992 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttJamaican" -version = "0.13.2" +version = "0.13.3" description = "Base package for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]