Compare commits
2 commits
e9507fb5a4
...
cb147c203d
Author | SHA1 | Date | |
---|---|---|---|
cb147c203d | |||
4c51189d41 |
|
@ -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
|
||||
|
|
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.upgrades
|
||||
db.sess
|
||||
db.util
|
||||
email
|
||||
email.handler
|
||||
email.message
|
||||
|
|
|
@ -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"}]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
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
|
||||
|
||||
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
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