Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object has no attribute 'drivername' if using only binds, not default DB #819

Open
colegleason opened this issue May 13, 2020 · 2 comments
Open

Comments

@colegleason
Copy link

@colegleason colegleason commented May 13, 2020

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
@CoburnJoe
Copy link
Contributor

@CoburnJoe CoburnJoe commented Jun 1, 2020

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?

@colegleason
Copy link
Author

@colegleason colegleason commented Jun 9, 2020

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")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.