fix: add make_proxy()
convenience method for data model Base
This commit is contained in:
parent
2289928337
commit
4a6897c6de
|
@ -25,19 +25,103 @@ Base Models
|
||||||
|
|
||||||
.. class:: Base
|
.. class:: Base
|
||||||
|
|
||||||
This is the base class for all data models.
|
This is the base class for all :term:`data models <data model>` in
|
||||||
|
the :term:`app database`. You should inherit from this class when
|
||||||
|
defining custom models.
|
||||||
|
|
||||||
|
This class inherits from :class:`WuttaModelBase`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
|
||||||
from wuttjamaican.db.util import (naming_convention, ModelBase,
|
from wuttjamaican.db.util import (naming_convention, ModelBase,
|
||||||
uuid_column, uuid_fk_column)
|
uuid_column, uuid_fk_column)
|
||||||
|
|
||||||
|
|
||||||
|
class WuttaModelBase(ModelBase):
|
||||||
|
"""
|
||||||
|
Base class for data models, from which :class:`Base` inherits.
|
||||||
|
|
||||||
|
Custom models should inherit from :class:`Base` instead of this
|
||||||
|
class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def make_proxy(cls, main_class, extension, name, proxy_name=None):
|
||||||
|
"""
|
||||||
|
Convenience method to declare an "association proxy" for the
|
||||||
|
main class, per the params.
|
||||||
|
|
||||||
|
For more info see
|
||||||
|
:doc:`sqlalchemy:orm/extensions/associationproxy`.
|
||||||
|
|
||||||
|
:param main_class: Reference to the "parent" model class, upon
|
||||||
|
which the proxy will be defined.
|
||||||
|
|
||||||
|
:param extension: Attribute name on the main class, which
|
||||||
|
references the extension record.
|
||||||
|
|
||||||
|
:param name: Attribute name on the extension class, which
|
||||||
|
provides the proxied value.
|
||||||
|
|
||||||
|
:param proxy_name: Optional attribute name on the main class,
|
||||||
|
which will reference the proxy. If not specified, ``name``
|
||||||
|
will be used.
|
||||||
|
|
||||||
|
As a simple example consider this model, which extends the
|
||||||
|
:class:`~wuttjamaican.db.model.auth.User` class. In
|
||||||
|
particular note the last line which is what we're documenting
|
||||||
|
here::
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
|
from wuttjamaican.db import model
|
||||||
|
|
||||||
|
class PoserUser(model.Base):
|
||||||
|
\""" Poser extension for User \"""
|
||||||
|
__tablename__ = 'poser_user'
|
||||||
|
|
||||||
|
uuid = model.uuid_column(sa.ForeignKey('user.uuid'), default=None)
|
||||||
|
user = orm.relationship(
|
||||||
|
model.User,
|
||||||
|
doc="Reference to the main User record.",
|
||||||
|
backref=orm.backref(
|
||||||
|
'_poser',
|
||||||
|
uselist=False,
|
||||||
|
cascade='all, delete-orphan',
|
||||||
|
doc="Reference to the Poser extension record."))
|
||||||
|
|
||||||
|
favorite_color = sa.Column(sa.String(length=100), nullable=False, doc=\"""
|
||||||
|
User's favorite color.
|
||||||
|
\""")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.user)
|
||||||
|
|
||||||
|
# nb. this is the method call
|
||||||
|
PoserUser.make_proxy(model.User, '_poser', 'favorite_color')
|
||||||
|
|
||||||
|
That code defines a ``PoserUser`` model but also defines a
|
||||||
|
``favorite_color`` attribute on the main ``User`` class, such
|
||||||
|
that it can be used normally::
|
||||||
|
|
||||||
|
user = model.User(username='barney', favorite_color='green')
|
||||||
|
session.add(user)
|
||||||
|
|
||||||
|
user = session.query(model.User).filter_by(username='bambam').one()
|
||||||
|
print(user.favorite_color)
|
||||||
|
"""
|
||||||
|
proxy = association_proxy(
|
||||||
|
extension, proxy_name or name,
|
||||||
|
creator=lambda value: cls(**{name: value}))
|
||||||
|
setattr(main_class, name, proxy)
|
||||||
|
|
||||||
|
|
||||||
metadata = sa.MetaData(naming_convention=naming_convention)
|
metadata = sa.MetaData(naming_convention=naming_convention)
|
||||||
|
|
||||||
Base = orm.declarative_base(metadata=metadata, cls=ModelBase)
|
Base = orm.declarative_base(metadata=metadata, cls=WuttaModelBase)
|
||||||
|
|
||||||
|
|
||||||
class Setting(Base):
|
class Setting(Base):
|
||||||
|
|
|
@ -3,12 +3,34 @@
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
from wuttjamaican.db.model import base as mod
|
from wuttjamaican.db.model import base as mod
|
||||||
from wuttjamaican.db.model.auth import User
|
from wuttjamaican.db.model.auth import User
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
|
||||||
|
class MockUser(mod.Base):
|
||||||
|
__tablename__ = 'mock_user'
|
||||||
|
uuid = mod.uuid_column(sa.ForeignKey('user.uuid'), default=False)
|
||||||
|
user = orm.relationship(
|
||||||
|
User,
|
||||||
|
backref=orm.backref('_mock', uselist=False, cascade='all, delete-orphan'))
|
||||||
|
favorite_color = sa.Column(sa.String(length=100), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class TestWuttaModelBase(TestCase):
|
||||||
|
|
||||||
|
def test_make_proxy(self):
|
||||||
|
self.assertFalse(hasattr(User, 'favorite_color'))
|
||||||
|
MockUser.make_proxy(User, '_mock', 'favorite_color')
|
||||||
|
self.assertTrue(hasattr(User, 'favorite_color'))
|
||||||
|
user = User(favorite_color='green')
|
||||||
|
self.assertEqual(user.favorite_color, 'green')
|
||||||
|
|
||||||
|
|
||||||
class TestSetting(TestCase):
|
class TestSetting(TestCase):
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
|
@ -17,6 +39,7 @@ else:
|
||||||
setting.name = 'foo'
|
setting.name = 'foo'
|
||||||
self.assertEqual(str(setting), "foo")
|
self.assertEqual(str(setting), "foo")
|
||||||
|
|
||||||
|
|
||||||
class TestPerson(TestCase):
|
class TestPerson(TestCase):
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
|
|
Loading…
Reference in a new issue