SQLAlchemy inheritance not working - python

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")

Try adding
__table_args__ = {'extend_existing': True}
to your User class right under __tablename__=
cheers

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.

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:
Having left over .pyc files on the disk.
Git Ignoring empty directories full of files in .gitignore
Deleting the directories and cleaning the .pyc files solved the problem.

Related

db.create_all() not generating db

I'm trying to test Flask with SQLAlchemy and I stumbeld accross this problem. First, I have to note that I read all of the related threads and none of them solves my problem. I have a problem that db.create_all() doesn't generate the table I defined. I have model class in file person.py:
from website import db
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, nullable=False)
password = db.Column(db.String)
width = db.Column(db.Integer)
height = db.Column(db.Integer)
agent = db.Column(db.String)
user_data_dir = db.Column(db.String)
And in my website.py which is the file from where I launch the app:
from flask import Flask, jsonify, render_template, request
from flask_sqlalchemy import SQLAlchemy
# create the extension
db = SQLAlchemy()
def start_server(host, port, debug=False):
from person import Person
# create the app
app = Flask(__name__,
static_url_path='',
static_folder='web/static',
template_folder='web/templates')
# configure the SQLite database, relative to the app instance folder
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database0.db"
# initialize the app with the extension
db.init_app(app)
print('initialized db')
print('creating tables...')
with app.app_context():
db.create_all()
db.session.add(Person(username="example33"))
db.session.commit()
person = db.session.execute(db.select(Person)).scalar()
print('persons')
print(person.username)
if __name__ == '__main__':
start_server(host='0.0.0.0', port=5002, debug=True)
I think the problem might be that the Person class is not importing properly, because when I put the class inside the start_server function it executes fine and creates the table, but I don't know why this is happening. I followed all the advice and imported it before everything, and also I share the same db object between the 2 files
There is probably a better way to do this but this is the only way I could get this to work. You need to create a models.py file or w.e you wanna call it. Then all your database stuff goes in there. The db engine, ALL your models and a function to initialize it all. The reason is, you are having import issues where Person is imported but not fully and so the db doesn't have it in its metadata.
models.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, nullable=False)
password = db.Column(db.String)
width = db.Column(db.Integer)
height = db.Column(db.Integer)
agent = db.Column(db.String)
user_data_dir = db.Column(db.String)
# All other models
def initialize_db(app: Flask):
db.init_app(app)
with app.app_context():
db.create_all()
main.py
from flask import Flask
import models
def start_server(host, port, debug=False):
app = Flask(__name__)
# configure the SQLite database, relative to the app instance folder
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database0.db"
# initialize the app with the extension
models.initialize_db(app)
db = models.db
with app.app_context():
db.session.add(models.Person(username="example33"))
db.session.commit()
person = db.session.execute(db.select(models.Person)).scalar()
print('persons')
print(person.username)
if __name__ == '__main__':
start_server(host='0.0.0.0', port=5002, debug=True)
I am reading the documentation,
which explains that the function will
Create all tables stored in this metadata.
That leads me to believe Person is not associated with the db metadata.
You mentioned
when I put the class inside the start_server function it ... creates the table
Your from person import Person is nice enough,
but I suspect we wanted a simple import person.
In many apps the idiom would be import models.
Failing that, you may be able to point
create_all in the right direction
with this optional parameter:
tables – Optional list of Table objects, which is a subset of the total tables in the MetaData
Please let us know
what technical approach worked for you.

RuntimeError: No application found. Either work inside a view function or push an application context. FLASK SQLAlchemy error

application.py
from flask import Flask, render_template
from wtform_fields import *
from models import *
app = Flask(__name__)
app.secret_key='REPLACE LATER'
app.config['SQLALCHEMY_DATABASE_URI']='*db link*'
db = SQLAlchemy(app)
#app.route("/", methods=['GET', 'POST'])
def index():
reg_form = RegistrationForm()
# Update database if validation success
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
user_object = User.query.filter_by(username=username).first()
if user_object:
return "Already Taken"
user =User(username=username,password=password)
db.session.add(user)
db.session.commit()
return "Inserted into DB"
return render_template("index.html", form=reg_form)
if __name__ == "__main__":
app.run(debug=True)
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
""" User model """
__tablename__="users"
id=db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25) , unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
db.create_all()
I am getting this error:
D:\PostgreSQL\12\bin\GSTChat\venv\lib\site-packages\flask_sqlalchemy\__init__.py", line 1094, in create_all
self._execute_for_all_tables(app, bind, 'create_all')
File "D:\PostgreSQL\12\bin\GSTChat\venv\lib\site-packages\flask_sqlalchemy\__init__.py", line 1071, in _execute_for_all_tables
app = self.get_app(app)
File "D:\PostgreSQL\12\bin\GSTChat\venv\lib\site-packages\flask_sqlalchemy\__init__.py", line 1042, in get_app
raise RuntimeError(
**RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.**
I even looked for solution in the documentation but , I am not able to resolve the error.
EDIT: Adding another answer...
You can have it as you already do in the models.py file, and initialize it later in your application file:
application.py
from flask import Flask, render_template
from wtform_fields import *
from models import *
app = Flask(__name__)
# Other code you have
db.init_app(app)
Answer 2: I see you are defining db in your application.py file, and again in your models.py file. You should import it from your app in your models file.
models.py
from flask_sqlalchemy import SQLAlchemy
from app import db
class User(db.Model):
""" User model """
__tablename__="users"
id=db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25) , unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
I hope this is helpful and solves your error! :) You may also want to put the db.create_all() in your application.py file (after importing models), instead of in a model itself. Cheers.

How can I create a table if not exist on Flask with SQLAlchemy?

I am using SQLAlchemy and I have the following code:
Model:
class User(db.Model):
__tablename__ = 'user'
__table_args__ = {'schema': 'task', 'useexisting': True}
id = Column(Integer, primary_key=True, autoincrement=True)
firstname = Column(String)
.env
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI')
app.py
def create_app(config_file):
"""Create a Flask application using the app factory pattern."""
app = Flask(__name__)
"""Load configuration."""
app.config.from_pyfile(config_file)
"""Init app extensions."""
from .extensions import db
db.init_app(app)
This creates the SQLite file if it does not exist, but not the tables of each model.
The question is what can I do in order to create the tables for each model?
Just add:
db.create_all()
in app.py at the end of create_app().
create_all() will create the tables only when they don't exist and would not change the tables created before.
If you want to create the database and the tables from the command line you can just type:
python
from app.py import db
db.create_all()
exit()
The working example:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.secret_key = "Secret key"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///my_database.sqlite3"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Data(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(50))
email = db.Column(db.String(50))
phone = db.Column(db.String(50))
db.create_all()
# add a row
# comment out after the 1st run
table_row = Data(name="My Name", email="myemail#mail.com", phone="123456")
db.session.add(table_row)
db.session.commit()
print "A row was added to the table"
# read the data
row = Data.query.filter_by(name="My Name").first()
print "Found:", row.email, row.phone
if __name__ == "__main__":
app.run(debug=True)
This is for Python 2.7, to run with Python 3.x just change the the print statements to call the print() function.
NOTE:
When using automatic model class constructor the arguments passed to model class constructor must be keyword arguments or there will be an error. Otherwise you can override the __init__() inside Data() class like this:
def __init__(self, name, email, phone, **kwargs):
super(Data, self).__init__(**kwargs)
self.name = name
self.email = email
self.phone = phone
In that case you don't have to use keyword arguments.
you need first to use Shell Context Processor to load automatically all Model objects
in app.py add
# import all models from all blueprints you have
from .users.models import User
#app.shell_context_processor
def make_shell_context():
return { 'db': db, 'User': User .. }
and then use Flask shell command
(venv) $ flask shell
>>> db
<SQLAlchemy engine=sqlite:///data-dev.sqlite> # something similar to that
>>>
>>> User
<class 'api.users.models.User'>
>>>
>>> # to create database if not exists and all tables, run the command below
>>> db.create_all()
maybe you'll need Flask-Migrate for advanced operations (migrations) on your database: create new table, update tables / fields ...

use db without requiring app.app_context()- Flask

I have an app like this:
myapp/app/init.py:
import sqlite3
from contextlib import closing
from flask import Flask, g
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
# from app.models import db
from database import db
application = Flask(__name__)
application.config.from_object('config')
application.debug = True
db.init_app(application)
login_manager = LoginManager()
login_manager.init_app(application)
from app import views
myapp/database.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
myapp/app/models.py:
from database import db
from app import application
class CRUDMixin(object):
...
def delete(self, commit=True):
"""Remove the record from the database."""
with application.app_context():
db.session.delete(self)
return commit and db.session.commit()
class Model(CRUDMixin, db.Model):
"""Base model class that includes CRUD convenience methods."""
__abstract__ = True
def __init__(self, **kwargs):
db.Model.__init__(self, **kwargs)
class User(Model):
"""
:param str email: email address of user
:param str password: encrypted password for the user
"""
__tablename__ = 'users'
email = db.Column(db.String, primary_key=True)
password = db.Column(db.String)
authenticated = db.Column(db.Boolean, default=False)
def is_active(self):
"""True, as all users are active."""
return True
def get_id(self):
"""Return the email address to satisfy Flask-Login's requirements."""
return self.email
def is_authenticated(self):
"""Return True if the user is authenticated."""
return self.authenticated
def is_anonymous(self):
"""False, as anonymous users aren't supported."""
return False
The project I tried to structure after did not require with application.app_context() in the Model helper class. I cannot see any significant differences between my setup and its, and yet without with application.app_context() all over anything related to db I get the usual application not registered on db error. When everything you see in app/models.py and database.py was in app/__init__.py, it worked without requiring any with application.app_context() and I could import db raw in the shell like from myapp.app import db and it worked as is. What can I do to quiet the application not registered on db complaint but be able to use db easily without needing app_context, but still keep a proper directory structure where everything isn't jammed into init? Thank you
Flask-Script gives you a shell.
If you want to do it without Flask-Script, you must set the application context. A normal Python shell doesn't know how to setup your context.
It is easy to mimic the Flask-Script shell.
Create a shell.py file:
from app import application
ctx = application.app_context()
ctx.push()
Run it with python -i and use db with your app context already defined:
$ python -i shell.py
>>> from app import db
>>> db.create_all()

Flask Project Structure Change

Hello I have a finished Flask/Flask-SqlAlchemy app working correctly but I wanted to reorganize its structure according to:
http://flask.pocoo.org/docs/0.10/patterns/packages/
My working project has the following structure and files:
folder:monkeyMeRoundTwo:
-app.py
-config.py
-models.py
-monkeyDB.db
-app_test.py
folder:templates:
-edit.html
-friends.html
-layout.html
-login.html
-myProfile.html
-profile.html
-register.html
-users.html
folder:static:
folder:css
folder:images
My app.py file:
from flask import Flask, render_template, redirect, \
url_for, request, session, flash
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
# local
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///monkeyDB.db'
# heroku
#import os
app.config.from_object('config.BaseConfig')
#app.config.from_object(os.environ['APP_SETTINGS'])
db = SQLAlchemy(app)
from models import *
#app.route('/')
def index():
if not session.get('logged_in'):
return render_template('login.html')
else:
return redirect(url_for('friendList'))
.......rest of views
if __name__ == '__main__':
app.run(debug=True)
My config.py file:
import os
class BaseConfig(object):
SECRET_KEY = "kjdsbfkjgdf78sft"
My models.py file:
from app import db
from sqlalchemy.orm import relationship, backref
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
class users(db.Model):
__tablename__ = "Users"
id = db.Column(db.Integer, primary_key=True)
userName = db.Column(db.String, nullable=False)
userEmail = db.Column(db.String, nullable=False)
userPhone = db.Column(db.String, nullable=False)
userPass = db.Column(db.String, nullable=False)
def __init__(self, userName, userEmail, userPhone, userPass):
self.userName = userName
self.userEmail = userEmail
self.userPhone = userPhone
self.userPass = userPass
def __repr__(self):
return '{}-{}-{}-{}'.format(self.id, self.userName, self.userEmail, self.userPhone)
class friendships(db.Model):
__tablename__ = "Friendships"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
friend_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
userR = db.relationship('users', foreign_keys='friendships.user_id')
friendR = db.relationship('users', foreign_keys='friendships.friend_id')
def __init__(self, user_id, friend_id):
self.user_id = user_id
self.friend_id = friend_id
def __repr__(self):
return '{}-{}-{}-{}'.format(self.user_id, self.friend_id)
class bestFriends(db.Model):
__tablename__ = "BestFriends"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
best_friend_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
user = db.relationship('users', foreign_keys='bestFriends.user_id')
best_friend = db.relationship('users', foreign_keys='bestFriends.best_friend_id')
def __init__(self, user_id, best_friend_id):
self.user_id = user_id
self.best_friend_id = best_friend_id
def __repr__(self):
return '{}-{}-{}-{}'.format(self.user_id, self.best_friend_id)
My app_tests.py file:
import os
import app
import unittest
import tempfile
class AppTestCase(unittest.TestCase):
def setUp(self):
self.db_fd, app.app.config['DATABASE'] = tempfile.mkstemp()
app.app.config['TESTING'] = True
self.app = app.app.test_client()
def tearDown(self):
os.close(self.db_fd)
os.unlink(app.app.config['DATABASE'])
def test_index_page(self):
rv = self.app.get('/')
assert 'Friends' in rv.data
...........rest of tests
if __name__ == '__main__':
unittest.main()
As I mentioned everything works fine and the tests pass all ok but when I attempt to follow the example on:
http://flask.pocoo.org/docs/0.10/patterns/packages/
and change the project to the following structure:
Structure/organization:
folder:monkeyMeRT:
-runserver.py
folder:monkeyMeRT:
-__init__.py
-views.py
-config.py
-models.py
-monkeyDB.db
-app_test.py
folder:templates:
-edit.html
-friends.html
-layout.html
-login.html
-myProfile.html
-profile.html
-register.html
-users.html
folder:static:
folder:css
folder:images
runserver.py file:
from monkeyMeRT import app
app.run(debug=True)
init.py file:
from flask import Flask, render_template, redirect, \
url_for, request, session, flash
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
# local
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///monkeyDB.db'
# heroku
#import os
app.config.from_object('config.BaseConfig')
#app.config.from_object(os.environ['APP_SETTINGS'])
db = SQLAlchemy(app)
from models import *
import monkeyMeRT.views
views.py file:
from monkeyMeRT import app
.......here go all the views/functions
models.py file:
from monkeyMeRT import db
from sqlalchemy.orm import relationship, backref
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
........here go the db classes
config.py file:
import os
class BaseConfig(object):
SECRET_KEY = "kjdsbfkjgdf78sft"
After changes I end up with errors.
Traceback (most recent call last):
File "__init__.py", line 19, in <module>
from models import *
File "/Users/alex/Desktop/PY/monkeyMeRT/monkeyMeRT/models.py", line 1, in <module>
from monkeyMeRT import db
ImportError: No module named monkeyMeRT
I guess I haven't interpreted the example correctly. Would anyone be able to help me out with this I am new to Flask(started learning a month ago) and would like to have an efficient setup of the project with config and views separated like in the example:
http://flask.pocoo.org/docs/0.10/patterns/packages/
Help is very much appreciated!
Thanks in advance.
The traceback is telling you that monkeyMeRT is not a package. To fix this:
Be sure to name your project folder monkeyMeRT
monkeyMeRT needs to contain a file called __init__.py
That init file doesn't have to contain anything, it just needs to exist so that Python knows to treat the directory as a Python package.

Categories