1
0
Fork 0

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
This commit is contained in:
Lance Edgar 2024-08-30 20:30:09 -05:00
parent e9507fb5a4
commit 4c51189d41
7 changed files with 134 additions and 82 deletions

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.base
db.model.upgrades db.model.upgrades
db.sess db.sess
db.util
email email
email.handler email.handler
email.message email.message

View file

@ -27,8 +27,6 @@ This is the default :term:`app model` module.
The ``wuttjamaican.db.model`` namespace contains the following: 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.Base`
* :class:`~wuttjamaican.db.model.base.Setting` * :class:`~wuttjamaican.db.model.base.Setting`
* :class:`~wuttjamaican.db.model.base.Person` * :class:`~wuttjamaican.db.model.base.Person`
@ -39,6 +37,9 @@ The ``wuttjamaican.db.model`` namespace contains the following:
* :class:`~wuttjamaican.db.model.upgrades.Upgrade` * :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 .auth import Role, Permission, User, UserRole
from .upgrades import Upgrade from .upgrades import Upgrade

View file

@ -31,62 +31,15 @@ Base Models
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm 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) 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) 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): class Setting(Base):
""" """
Represents a :term:`config setting`. 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 from unittest import TestCase
try: try:
import sqlalchemy as sa from wuttjamaican.db.model import base as mod
from wuttjamaican.db.model import base as model
from wuttjamaican.db.model.auth import User from wuttjamaican.db.model.auth import User
except ImportError: except ImportError:
pass pass
else: 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): class TestSetting(TestCase):
def test_basic(self): def test_basic(self):
setting = model.Setting() setting = mod.Setting()
self.assertEqual(str(setting), "") self.assertEqual(str(setting), "")
setting.name = 'foo' setting.name = 'foo'
self.assertEqual(str(setting), "foo") self.assertEqual(str(setting), "foo")
@ -46,13 +20,13 @@ else:
class TestPerson(TestCase): class TestPerson(TestCase):
def test_basic(self): def test_basic(self):
person = model.Person() person = mod.Person()
self.assertEqual(str(person), "") self.assertEqual(str(person), "")
person.full_name = "Barney Rubble" person.full_name = "Barney Rubble"
self.assertEqual(str(person), "Barney Rubble") self.assertEqual(str(person), "Barney Rubble")
def test_users(self): def test_users(self):
person = model.Person() person = mod.Person()
self.assertIsNone(person.user) self.assertIsNone(person.user)
user = 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)