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:
parent
e9507fb5a4
commit
4c51189d41
6
docs/api/wuttjamaican/db.util.rst
Normal file
6
docs/api/wuttjamaican/db.util.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttjamaican.db.util``
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. automodule:: wuttjamaican.db.util
|
||||||
|
:members:
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
77
src/wuttjamaican/db/util.py
Normal file
77
src/wuttjamaican/db/util.py
Normal 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)
|
|
@ -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
40
tests/db/test_util.py
Normal 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)
|
Loading…
Reference in a new issue