flask-sqlalchemy
flask-sqlalchemy copied to clipboard
object has no attribute 'drivername' if using only binds, not default DB
Expected Behavior
I have a flask project with two binds set in SQLALCHEMY_BINDS and with SQLALCHEMY_DATABASE_URI not specified. I would like this to work as long as __bind_key__ is properly specified for each model. I don't want to have a default DB, as I think that might lead to mistakes.
@pytest.fixture
def app():
db_1_fd, db_1 = tempfile.mkstemp()
db_2_fd, db_2 = tempfile.mkstemp()
class TestingConfig(Config):
DEBUG = True
TESTING = True
SQLALCHEMY_BINDS = {
'db1': 'sqlite:///' + db_1,
'db2': 'sqlite:///' + db_2,
}
app = backend.create_app(TestingConfig)
with app.app_context():
db.create_all(bind=['db1', 'db2'])
yield app
os.close(db_1_fd)
os.close(db_2_fd)
os.unlink(db_1)
os.unlink(db_2)
def test_get_user(app):
with app.app_context():
# no user exists yet
user = User.query.first()
assert user == None
Actual Behavior
It attempts to call apply_driver_hacks with sa_url = None. How can I prevent apply_driver_hacks from being called when SQLALCHEMY_DATABASE_URI is not set?
=============================================================== test session starts ================================================================
platform darwin -- Python 3.7.4, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: <redacted>
collected 1 item
backend/tests/test_<redacted>.py F [100%]
===================================================================== FAILURES =====================================================================
__________________________________________________________________ test_get_user ___________________________________________________________________
self = <sqlalchemy.util._collections.ScopedRegistry object at 0x11bc48a50>
def __call__(self):
key = self.scopefunc()
try:
> return self.registry[key]
E KeyError: 4576705984
../../.virtualenvs/<redacted>-mTJqo3Um/lib/python3.7/site-packages/sqlalchemy/util/_collections.py:1010: KeyError
During handling of the above exception, another exception occurred:
app = <Flask 'backend'>
def test_get_user(app):
with app.app_context():
# no user exists yet
> user = User.query.first()
backend/tests/test_<redacted>.py:83:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:518: in __get__
return type.query_class(mapper, session=self.sa.session())
../../.virtualenvs/<redacted>-mTJqo3Um/lib/python3.7/site-packages/sqlalchemy/orm/scoping.py:78: in __call__
return self.registry()
../../.virtualenvs/<redacted>-mTJqo3Um/lib/python3.7/site-packages/sqlalchemy/util/_collections.py:1012: in __call__
return self.registry.setdefault(key, self.createfunc())
../../.virtualenvs/<redacted>-mTJqo3Um/lib/python3.7/site-packages/sqlalchemy/orm/session.py:3213: in __call__
return self.class_(**local_kw)
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:138: in __init__
bind = options.pop('bind', None) or db.engine
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:929: in engine
return self.get_engine()
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:948: in get_engine
return connector.get_engine()
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:559: in get_engine
options = self.get_options(sa_url, echo)
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:574: in get_options
self._sa.apply_driver_hacks(self._app, sa_url, options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <SQLAlchemy engine=None>, app = <Flask 'backend'>, sa_url = None, options = {}
def apply_driver_hacks(self, app, sa_url, options):
"""This method is called before engine creation and used to inject
driver specific hacks into the options. The `options` parameter is
a dictionary of keyword arguments that will then be used to call
the :func:`sqlalchemy.create_engine` function.
The default implementation provides some saner defaults for things
like pool sizes for MySQL and sqlite. Also it injects the setting of
`SQLALCHEMY_NATIVE_UNICODE`.
"""
> if sa_url.drivername.startswith('mysql'):
E AttributeError: 'NoneType' object has no attribute 'drivername'
../../.virtualenvs/<redacted>-mTJqo3Um/src/flask-sqlalchemy/flask_sqlalchemy/__init__.py:869: AttributeError
Environment
- Operating system: macOS
- Python version: 3.7.4
- Flask-SQLAlchemy version: using master branch to include changes related to #663
- SQLAlchemy version: 1.3.4
I'd like to see a little more information. How are you choosing a DB in "normal" operation outside of tests? Is it possible to share some model code, with the bind key set?
Hi, here is an example of a model with the bind key set. All of my models have a bind key set for either one DB or another.
class User(db.Model):
__bind_key__ = 'twitter'
id = db.Column(db.String(36), nullable=False, primary_key=True)
screen_name = db.Column(db.String(36), nullable=True)
research_team = db.Column(db.Boolean, nullable=False, default=False)
consent = db.Column(db.Boolean, nullable=False, default=False)
reminders = db.Column(db.Boolean, nullable=False, default=True)
sessions = db.relationship("UserSession")
consent_form = db.relationship("ConsentForm")
@colegleason I think the quickest way to solve this issue may be to always set a SQLALCHEMY_DATABASE_URI, even if you then choose a bind for every request...
Should be fixed in #1087, which changed how engines are created and configured.