Skip to content

Commit

Permalink
Support for using existing declarative models (closes #60)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Knupp committed Apr 28, 2014
1 parent ab2147b commit 63598f7
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 7 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ Questions or comments about `sandman`? Hit me up at [jeff@jeffknupp.com](mailto:

# Changelog

## Version 0.9.6

* Support for using existing declarative models alongside `sandman` generated models
* If you have an existing app and want to include sandman in it, simply pass
your existing models in to the `register()` function along with any
`sanmdman` generated classes. `sandman` will detect the existing models
and augment them.

## Version 0.9.5

* Fixes a critical bug where code used by the new `etag` decorators was
Expand Down
11 changes: 11 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ jeff@jeffknupp.com.
Changelog
=========

Version 0.9.6
-------------

- Support for using existing declarative models alongside ``sandman``
generated models

- If you have an existing app and want to include sandman in it,
simply pass your existing models in to the ``register()`` function
along with any ``sanmdman`` generated classes. ``sandman`` will
detect the existing models and augment them.

Version 0.9.5
-------------

Expand Down
13 changes: 10 additions & 3 deletions docs/using_sandman.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ following:
- ``links``, ``primary_key``, and ``resource_uri`` methods that provide access
to various attributes of the object derived from the underlying database model

Creating a `models.py` file allows you to get *even more* out of ``sandman``. In the file,
create a class that derives from `sandman.models.Model` for each table you want to
Creating a ``models.py`` file allows you to get *even more* out of ``sandman``. In the file,
create a class that derives from ``sandman.models.Model`` for each table you want to
turn into a RESTful resource. Here's a simple example using the Chinook test database
(widely available online)::

Expand Down Expand Up @@ -160,7 +160,7 @@ turn into a RESTful resource. Here's a simple example using the Chinook test dat
Hooking up Models
-----------------

The `__tablename__` attribute is used to tell ``sandman`` which database table
The ``__tablename__`` attribute is used to tell ``sandman`` which database table
this class is modeling. It has *no default* and is *required* for all classes.

Providing a custom endpoint
Expand Down Expand Up @@ -329,4 +329,11 @@ following example, ``sandman``-related endpoints can be accessed by adding the
This allows both apps to coexist; ``my_app`` will be rooted at ``/`` and
``sandman`` at ``/sandman``.

Using existing declarative models
---------------------------------

If you have a Flask/SQLAlchemy application that already has a number of existing
declarative models, you can register these with ``sandman`` as if they were
auto-generated classes. Simply add your existing classes in the call to :func:`sandman.model.register`

.. _here: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch
20 changes: 16 additions & 4 deletions sandman/model/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,27 @@ class Model(object):
__table__ = None
"""Will be populated by SQLAlchemy with the table's meta-information."""

__from_class__ = None
"""Is this class being generated from an existing declarative SQLAlchemy
class?"""

@classmethod
def endpoint(cls):
"""Return the :class:`sandman.model.Model`'s endpoint.
:rtype: string
"""
endpoint = ''
if cls.__endpoint__ is not None:
return cls.__endpoint__
value = cls.__tablename__.lower()
if not value.endswith('s'):
value += 's'
return value
elif cls.__from_class__ is not None:
endpoint = cls.__from_class__.__name__.lower()
else:
endpoint = cls.__tablename__.lower()
if not endpoint.endswith('s'):
endpoint += 's'
return endpoint

def resource_uri(self):
"""Return the URI at which the resource can be found.
Expand All @@ -70,6 +78,8 @@ def primary_key(cls):
"""

if cls.__from_class__:
cls = cls.__from_class__
return cls.__table__.primary_key.columns.values()[0].name

def links(self):
Expand Down Expand Up @@ -150,6 +160,8 @@ def replace(self, dictionary):

@classmethod
def meta(cls):
if __from_class__:
cls = self.__from_class__
attribute_info = {}
for name, value in cls.__table__.columns.items():
attribute_info[name] = str(value.type).lower()
Expand Down
4 changes: 4 additions & 0 deletions sandman/model/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def register_internal_data(cls):
"""
with app.app_context():
if getattr(cls, 'endpoint', None) is None:
orig_class = cls
cls = type('Sandman' + cls.__name__, (cls, Model), {})
cls.__from_class__ = orig_class
current_app.class_references[cls.__tablename__] = cls
current_app.class_references[cls.__name__] = cls
current_app.class_references[cls.endpoint()] = cls
Expand Down
Binary file added tests/data/existing.sqlite3
Binary file not shown.

0 comments on commit 63598f7

Please sign in to comment.