AttributeError: can't set attribute when connecting to sqlite database with flask-sqlalchemy

Question:

I’ve been learning the flask web application framework and feel quite comfortable with it. I’ve previously built a simple to do app that worked perfectly. I was working on the same project, but trying to implement it using TDD. I’ve encountered an error with the database that I’ve never seen before and don’t know how to fix.

When I examine my code, I cant see any issue. It also looks identical to the code of the working project, so I really don’t know what I am doing wrong.

Here is the errors:

(env) PS C:coding-projectstask-master-tdd> flask shell
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:43:08) [MSC v.1926 32 bit (Intel)] on win32
App: project [development]
Instance: C:coding-projectstask-master-tddinstance
>>> from project import db
>>> db
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 1060, in __repr__
    self.engine.url if self.app or current_app else None
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 943, in engine
    return self.get_engine()
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 962, in get_engine
    return connector.get_engine()
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 555, in get_engine
    options = self.get_options(sa_url, echo)
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 570, in get_options
    self._sa.apply_driver_hacks(self._app, sa_url, options)
  File "c:coding-projectstask-master-tddenvlibsite-packagesflask_sqlalchemy__init__.py", line 914, in apply_driver_hacks
    sa_url.database = os.path.join(app.root_path, sa_url.database)
AttributeError: can't set attribute
>>>

my config.py file:

import os

# load the environment variables from the .env file
from dotenv import load_dotenv
load_dotenv()

# Determine the folder of the top-level directory of this project
BASEDIR = os.path.abspath(os.path.dirname(__file__))


class Config:
    FLASK_ENV = 'development'
    TESTING = False
    DEBUG = False
    SECRET_KEY = os.getenv('SECRET_KEY', default='A very terrible secret key.')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL',
                                        default=f"sqlite:///{os.path.join(BASEDIR, 'instance', 'app.db')}")
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL',
                                        default=f"sqlite:///{os.path.join(BASEDIR, 'instance', 'test.db')}")

class ProductionConfig(Config):
    FLASK_ENV = 'production'

my user model:

from project import db, login_manager
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model, UserMixin):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String, unique=True)
    hashed_password = db.Column(db.String)

    def __init__(self, username, password):
        self.username = username
        self.hashed_password = generate_password_hash(password)

    def is_password_valid(self, password):
        return check_password_hash(self.hashed_password, password)

    def __repr__(self):
        return '<User {}>'.format(self.id)

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
Asked By: Ange Uwase

||

Answers:

Just faced the exact same error. Turns out, I had the wrong Flask-SQLAlchemy library installed. I installed it using conda instead of pip, and conda installed the proper one for my operating system and environment.

conda install Flask-SQLAlchemy
Answered By: Jacob Sakhnini

I solved this problem by overriding apply_driver_hacks in the SQLAlchemy class.
In this new method I removed this line: sa_url.database = os.path.join(app.root_path, sa_url.database). I copied the rest of the method.

class MySQLAlchemy(SQLAlchemy):
    def apply_driver_hacks(self, app, sa_url, options):
        ...

The method can be found here. The fix I described should only be a temporary one. The problem might be fixed in newer versions of flask-sqlalchemy.

Answered By: alexander

Edit

If you’re experiencing this, upgrading Flask-SQLAlchemy to >= 2.5 should resolve the issue per https://github.com/pallets/flask-sqlalchemy/issues/910#issuecomment-802098285.

Pinning SQLAlchemy to ~1.3 should no longer be necessary.


I ran into this issue a little earlier, but think I’ve figured out what’s going on.

SQLAlchemy is automatically installed as a dependency for Flask-SQLAlchemy and its latest release (1.4.0) introduces the following breaking change:

The URL object is now an immutable named tuple. To modify a URL object, use the URL.set() method to produce a new URL object.

I was able to fix this issue by simply installing the previous version of SQL Alchemy (1.3.23).

Answered By: npburns224

Double check that this issue affects you by running

pip freeze

You should find that the current version of sqlalchemy is 1.4.0. I found the quickest solution for now is to manually revert to a previous version of sqlalchemy:

pip install SQLAlchemy==1.3.23

If you’ve just reverted to the previous version and it works then now is a great time to pin your versions:

pip freeze > requirements.txt
Answered By: sterne

I was able to fix this by pinning my sql-alchemy version to less than 1.4.0. Similar examples are listed above. Below are the steps that I did to solve this until a patch happens.

Freeze your packages

pip freeze > requirements.txt

Update your SQLAlchemy in your requirements.txt

SQLAlchemy<1.4.0

Reinstall the packages

pip install -r requirements.txt

For more information on how to do this refer to the pip documentation: https://pip.pypa.io/en/stable/

Answered By: gleek

Confirming npburns224’s / SuperShoot’s answer:
Upgrading Flask-SQLAlchemy (and thus its dependency SQLAlchemy) to the latest version did the trick for me.

pip install --upgrade flask-sqlalchemy

I am now running Flask-SQLAlchemy 2.5.1 and SQLAlchemy 1.4.3 successfully.

Answered By: Irrer Polterer