2
0
Fork 0

Compare commits

..

2 commits

Author SHA1 Message Date
Lance Edgar cb147c203d bump: version 0.13.2 → 0.13.3 2024-08-30 20:34:19 -05:00
Lance Edgar 4c51189d41 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
2024-08-30 20:30:09 -05:00
9 changed files with 141 additions and 83 deletions

View file

@ -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

View file

@ -0,0 +1,6 @@
``wuttjamaican.db.util``
========================
.. automodule:: wuttjamaican.db.util
:members:

View file

@ -17,6 +17,7 @@
db.model.base
db.model.upgrades
db.sess
db.util
email
email.handler
email.message

View file

@ -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"}]

View file

@ -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

View file

@ -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`.

View file

@ -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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
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)

View file

@ -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()

40
tests/db/test_util.py Normal file
View file

@ -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)