Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Released on February 27, 2017, codename Dubnium
the app context and its teardown event. (`#461`_)
- Tablename generation logic no longer accesses class properties unless they
are ``declared_attr``. (`#467`_)
- Added the ``auto_table_names`` arg to the ``SQLAlchemy`` constructor
which can be used to disable table name generation. (`#476`_)

.. _#328: https://github.com/mitsuhiko/flask-sqlalchemy/pull/328
.. _#364: https://github.com/mitsuhiko/flask-sqlalchemy/pull/364
Expand All @@ -27,6 +29,7 @@ Released on February 27, 2017, codename Dubnium
.. _#460: https://github.com/mitsuhiko/flask-sqlalchemy/pull/460
.. _#461: https://github.com/mitsuhiko/flask-sqlalchemy/pull/461
.. _#467: https://github.com/mitsuhiko/flask-sqlalchemy/pull/467
.. _#476: https://github.com/mitsuhiko/flask-sqlalchemy/pull/476

Version 2.1
-----------
Expand Down
31 changes: 25 additions & 6 deletions flask_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ def __init__(self, name, bases, d):
self.__table__.info['bind_key'] = bind_key


class _NoAutoBoundDeclarativeMeta(_BoundDeclarativeMeta):
def __new__(cls, name, bases, d):
d['__no_auto_tablename__'] = True
# skip _BoundDeclarativeMeta.__new__
return DeclarativeMeta.__new__(cls, name, bases, d)


def get_state(app):
"""Gets the state for the application"""
assert 'sqlalchemy' in app.extensions, \
Expand Down Expand Up @@ -640,6 +647,9 @@ class Model(object):

@declared_attr
def __tablename__(cls):
if hasattr(cls, '__no_auto_tablename__'):
del cls.__no_auto_tablename__
return
if (
'_cached_tablename' not in cls.__dict__ and
_should_set_tablename(cls)
Expand Down Expand Up @@ -721,15 +731,20 @@ class User(db.Model):
The `metadata` parameter was added. This allows for setting custom
naming conventions among other, non-trivial things.

.. versionadded:: 3.0
.. versionadded:: 2.2
The `query_class` parameter was added, to allow customisation
of the query class, in place of the default of :class:`BaseQuery`.

The `model_class` parameter was added, which allows a custom model
class to be used in place of :class:`Model`.

.. versionchanged:: 3.0
.. versionchanged:: 2.2
Utilise the same query class across `session`, `Model.query` and `Query`.

..versionadded:: 2.2
The `auto_table_names` parameter was added. This allows disabling the
table name generation logic and causes models with no name to fail as
it is the case in vanilla SQLAlchemy.
"""

#: Default query class used by :attr:`Model.query` and other queries.
Expand All @@ -738,12 +753,14 @@ class to be used in place of :class:`Model`.
Query = None

def __init__(self, app=None, use_native_unicode=True, session_options=None,
metadata=None, query_class=BaseQuery, model_class=Model):
metadata=None, query_class=BaseQuery, model_class=Model,
auto_table_names=True):

self.use_native_unicode = use_native_unicode
self.Query = query_class
self.session = self.create_scoped_session(session_options)
self.Model = self.make_declarative_base(model_class, metadata)
self.Model = self.make_declarative_base(model_class, metadata,
auto_table_names)
self._engine_lock = Lock()
self.app = app
_include_sqlalchemy(self, query_class)
Expand Down Expand Up @@ -796,11 +813,13 @@ class or a :class:`~sqlalchemy.orm.session.sessionmaker`.

return orm.sessionmaker(class_=SignallingSession, db=self, **options)

def make_declarative_base(self, model, metadata=None):
def make_declarative_base(self, model, metadata=None, auto_table_names=True):
"""Creates the declarative base."""
metaclass = (_BoundDeclarativeMeta if auto_table_names
else _NoAutoBoundDeclarativeMeta)
base = declarative_base(cls=model, name='Model',
metadata=metadata,
metaclass=_BoundDeclarativeMeta)
metaclass=metaclass)

if not getattr(base, 'query_class', None):
base.query_class = self.Query
Expand Down
11 changes: 11 additions & 0 deletions test_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import flask
import sqlalchemy as sa
from sqlalchemy.exc import InvalidRequestError
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import sessionmaker

Expand Down Expand Up @@ -369,6 +370,16 @@ def floats(self):

self.assertTrue(ns.accessed)

def test_no_auto_generation(self):
app = flask.Flask(__name__)
db = fsa.SQLAlchemy(app, auto_table_names=False)

def _fn():
class Unnamed(db.Model):
id = db.Column(db.Integer, primary_key=True)

self.assertRaises(InvalidRequestError, _fn)


class PaginationTestCase(unittest.TestCase):
def test_basic_pagination(self):
Expand Down