23

I'm using Flask and SQLAlchemy. I have used my own abstract base class and inheritance. When I try to use my models in the python shell I get the following error:

>>> from schedule.models import Task
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/teelf/projects/schedule/server/schedule/models.py", line 14, in <module>
    class User(Base):
  File "/home/teelf/projects/schedule/server/venv/lib/python3.4/site-packages/flask_sqlalchemy/__init__.py", line 536, in __init__
    DeclarativeMeta.__init__(self, name, bases, d)
  File "/home/teelf/projects/schedule/server/venv/lib/python3.4/site-packages/sqlalchemy/ext/declarative/api.py", line 55, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/home/teelf/projects/schedule/server/venv/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 254, in _as_declarative
    **table_kw)
  File "/home/teelf/projects/schedule/server/venv/lib/python3.4/site-packages/sqlalchemy/sql/schema.py", line 393, in __new__
    "existing Table object." % key)
sqlalchemy.exc.InvalidRequestError: Table 'user' is already defined for this MetaData instance.  Specify 'extend_existing=True' to redefine options and columns
 on an existing Table object.

How do I fix this?

Code:

manage.py:

#!/usr/bin/env python

import os, sys

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from flask.ext.migrate import Migrate, MigrateCommand
from flask.ext.script import Manager

from server import create_app
from database import db


app = create_app("config")

migrate = Migrate(app, db)
manager = Manager(app)

manager.add_command("db", MigrateCommand)

if __name__ == "__main__":
    manager.run()

__init__.py:

from flask import Flask

from flask.ext.login import LoginManager

from database import db
from api import api
from server.schedule.controllers import mod_schedule


def create_app(config):
    # initialize Flask
    app = Flask(__name__)

    # load configuration file
    app.config.from_object(config)

    # initialize database
    db.init_app(app)

    api.init_app(app)

    # initialize flask-login
    login_manager = LoginManager(app)

    # register blueprints
    app.register_blueprint(mod_schedule)

    return app

database.py:

from flask.ext.sqlalchemy import SQLAlchemy


db = SQLAlchemy()

models.py:

from sqlalchemy.dialects.postgresql import UUID

from database import db


class Base(db.Model):
    __abstract__ = True

    id = db.Column(UUID, primary_key=True)


class User(Base):
    __tablename__ = "user"

    username = db.Column(db.String)
    password = db.Column(db.String)
    first_name = db.Column(db.String)
    last_name = db.Column(db.String)
    authenticated = db.Column(db.Boolean, default=False)

    def __init__(self, first_name, last_name, username):
        self.first_name = first_name
        self.last_name = last_name
        self.username = username

    def is_active(self):
        """ All users are active """
        return True

    def get_id(self):
        return self.username

    def is_authenticated(self):
        return self.authenticated

    def is_anonymous(self):
        """ Anonymous users are not supported"""
        return False

controllers.py:

from flask import Blueprint

from flask.ext.restful import reqparse, Resource

from api import api
from server.schedule.models import User


mod_schedule = Blueprint("schedule",  __name__, url_prefix="/schedule")


class Task(Resource):
    def put(self):
        pass

    def get(self):
        pass

    def delete(self):
        pass


api.add_resource(Task, "/tasks/<int:id>", endpoint="task")
6
  • Two things that might be helpful (although I haven't had the chance to test them): a) when initializing SQLAlchemy() use your Flask app (e.g. db = SQLAlchemy(app); b) users is a reserved key word in PostgreSQL, so maybe __tablename__ = "users" might end up in some conflict. Commented Jan 7, 2015 at 11:00
  • @cuducos a) I have db.init_app(app) in __init__.py. b) the tablename in my code is user not users. Commented Jan 7, 2015 at 15:49
  • @teelf How are you managing migrations/creation of the tables? Is it possible to share your __init__.py or more bits of your application? Commented Jan 9, 2015 at 8:53
  • 1
    @teelf, not good news: I replicated all your code locally and ran the import with no errors: In [1]: from server.schedule.models import Task \n In [2]: Probably the error is elsewhere… Commented Jan 10, 2015 at 19:28
  • 1
    Is there more code in models.py that you haven't posted? Commented Feb 16, 2015 at 4:08

3 Answers 3

64

Try adding

 __table_args__ = {'extend_existing': True} 

to your User class right under __tablename__=

cheers

Sign up to request clarification or add additional context in comments.

3 Comments

Where did you learn this, and can you share in the SQLalchemy docs where is case is spoken about more? (You answer is just what I needed)
While this is useful in some cases, in general this should not be needed and is a sign that the table definitions are created multiple times – usually by accident. Throwing it as a generic "just copy and paste this to your code" solution is misleading and just hides the real errors.
Here's a link to sqlalchemy docs, where __table_args__ dict is mentioned.
0

Another option, if you don't already have data in your pre-existing database, is to drop it, recreate it without the tables. Then interactively run your "models.py" script (you would need to add a little code at the bottom to allow this), then in the interactive Python console do "db.create_all()" and it should create the tables based on your classes.

Comments

0

I had the same problem and found the solution here: https://github.com/pallets-eco/flask-sqlalchemy/issues/672#issuecomment-478195961

So, basically we hit the union of two problems:

  1. Having left over .pyc files on the disk.
  2. Git Ignoring empty directories full of files in .gitignore

Deleting the directories and cleaning the .pyc files solved the problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.