scoped_session object has no attribute 'create_all' - python

I have the following code that sets up my database with Flask-SQLAlchemy. I'm getting an exception "AttributeError: scoped_session object has no attribute 'create_all'". Can someone please explain to me why I'm getting the error and how I can fix it? :)
__init__.py:
from flask import Flask
app = Flask(__name__)
from app.db import DB
database = DB()
database.create_all()
db/__init__.py:
from flask import session
from flask.ext.sqlalchemy import SQLAlchemy
from app import app
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:pass#dburl:/schema'
connection = SQLAlchemy(app)
from app.db.models import *
class DB():
def __init__(self):
pass
def create_all(self):
connection.session.create_all()
connection.session.commit()
print("done")
models.py:
from app.db import connection as db
class Test(db.Model):
__tablename__ = 'Test'
id = db.Column("ID", db.Integer, primary_key=True)
name = db.Column("Name", db.String(100), nullable=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Test: %s>' % self.name
Any guidance would be greatly appreciated!

It's connection.create_all(), you added session in the middle.
Unrelated to the immediate problem, there are other things that don't look right.
You don't need to commit after running create_all.
The extension instance is usually named db. The DB class does nothing, just write your create_all function on its own.
You don't need to specify table or column names for Flask-SQLAlchemy models in most cases, and upper case names are not generally used.
Don't use tabs, PEP 8 recommends 4 spaces.

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.

Flask_SQLAlchemy modularization issues due ORM

I am trying to build an API using Flask. For database actions I use flask_sqlalchemy.
In my main file, the flask app is initalized. I pass the resulting instance to another file where the configuration is set and to my database module that handles database operations.
main.py:
app = flask.Flask(__name__) # initialize flask app
#initialize modules with app
config.init(app)
database.init(app)
The problem is, the relations I use in the database are in a seperate file and it needs the db object to declare the classes for ORM.
My idea was to declare db and initialize it later in an init function, but that doesn't work in this case, because the db object is undefined when the pythonfile is loaded by an import.
relations.py
db: SQLAlchemy
def init(db):
Relations.db = db
class Series(db.Model):
"""Representation of a series
"""
id = db.Column(db.String(255), primary_key=True)
title = db.Column(db.String(255))
class User(db.Model):
"""Representation of a user
"""
id = db.Column(db.INT, primary_key=True)
name = db.Column(db.String(255))
class Subscription(db.Model):
"""Representation of a subscription
"""
series_id = db.Column(db.INT, primary_key=True)
user_id = db.Column(db.String(255), primary_key=True)
My database module uses the way and it works fine(init.py file):
db: SQLAlchemy
def init(app):
database.db = SQLAlchemy(app)
# handle database operations...
One approach to solve the issue is just using another instance in the relations.py like that:
app = flask.Flask(__name__)
db = SQLAlchemy(app)
# declare classes...
I tried it out and it workes, but that is not a nice way to solve this and leads to other problems.
Importing it from main does also not work because of circular import.
I have no idea how to smoothly solve this without removing modularization. I would be thankful for any inputs. If I should add any further information, just let me know.
I would create the app variable in your main.py file but leave out the initializing part. From there you call a function from init.py to basically set up the database. That is what I did for my last flask project.
Main.py:
from init import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
Init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
DB_NAME = "database.db"
def create_app():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}'
db.init_app(app)
create_database(app)
#Other operations ...
return app
Relations.py
from init import db
#all your classes ...
db.create_all()
So now you can import the db object to your relations.py file from the init.py.

Flask_SQLAlchemy, db.create_all() is unable to "see" my tables when imported though a service class

The intent: Refactor my code into MVC (this is just the model/database part), and have the server create the database with tables on first run if the database or tables does not exist.
This works when using a "flat" file with all the classes and functions defined in that file, but after moving out the functions into a service class and the models into their own folder with model classes, the db.create_all() function does not seem to be able to detect the table class correctly any more.
Example structure, (minimum viable problem):
server.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
def main():
# Intentionally moved into the main() function to prevent import loops
from services.users import UserService
users = UserService(db)
db.create_all()
app.run(debug=True)
if __name__ == '__main__':
main()
services\users.py
# Class used to access users data using a session
from models.users import Users
class UserService:
def __init__(self, db):
self.db = db
def get_all(self):
return self.db.session.query(Users).all()
def get(self, uid):
return self.db.session.query(Users).get(uid)
def add(self, json):
user = Users(email=json['email'], password=json['password'])
self.db.session.add(user)
self.db.session.commit()
return user
models\users.py
# The actual model
from server import db
class Users(db.Model):
_id = db.Column("id", db.Integer, primary_key=True)
email = db.Column(db.Text)
password = db.Column(db.Text)
Result: The database is created, but it is just an empty file with no tables inside of it.
I have also tried placing the db.create_all() inside the service class def __init__(self, db) (grasping at straws here), both as a self reference and as an argument reference. Neither have worked.
I am sure it is something obvious I am missing, but I have boiled down my project to just the bare minimum and still fail to see why it is not working - so I have to ask. How can I get the db.create_all() to detect my table classes correctly and actually create the required tables, while using this code structure (or something similar, in case I have misunderstood MVC)?
The problem is that server.py is executed twice
when it's imported in models/users.py
when server.py is called to run the app
Each execution generates a new db instance. The db imported by the model file adds the models to its metadata, the db created when the app is run has empty metadata.
You can confirm this by printing id(db) and db.metadata.tables at the end of models/users.py and just before the call to db.create_all() in the main function.
You need to structure your code so that only one db gets created. For example, you could move the app configuration and creation code into its own module, mkapp.py (feel free to come up with a better name):
mkapp.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
And in server.py do
from mkapp import app, db
and in models/users.py do
from mkapp import db
As a bonus, this should also remove the import cycle.
I don't use flask much, so this solution can probably be improved on. For example, having a function create app and db and memoise the results might be better than creating them in top-level module code.

NoneType has no attribute (import confusion)

I set a database instance shared by many models:
Database.py:
from flask_sqlalchemy import SQLAlchemy
from models.shared import load_db
def init_db(app):
db = SQLAlchemy(app)
load_db(db)
from models.user import User
from models.another_model import AnotherModel
from ...
init_db is called from create_app in my main server.py file.
Shared.py:
db = None
def load_db(_db):
db = _db
print(db)
User.py:
from .shared import db
print("User model defined.")
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(15), unique=True, nullable=False)
hash = db.Column(db.String(32), nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
When init_db is called from the create_app method in my main file I get:
AttributeError: 'NoneType' object has no attribute 'Model'
Why am I getting this error? When importing the model modules from inside init_db shouldn't their code run after I call load_db? Looking at the order of the print statements db is clearly set before any model uses it:
<SQLAlchemy engine=mysql://root:***#localhost/db_name?charset=utf8>
User model defined.
In
def load_db(_db):
db = _db
print(db)
You're actually declaring a variable db local to the function load_db.
To actually refer to global db variable try global db expression:
def load_db(_db):
global db
db = _db
print(db)
I find this code pretty flaky, though, I would think about using singletones or some other approach.
Consider a case where you imported db before it has been initialized. Like:
from .shared import db
save_for_later_use(db)
Even if you replace the db in shared module, shared.db will point to the initialized value, while you'll have uninitialized value already pointed to by variable you stored somewhere in save_for_later_use.
In other words, you've replated what shared.db points to, but whichever code tried to remember shared.db before the initialization will still hold a pointer to None.

Using factory_boy with SQLAlchemy and class methods

I am working on a Pyramid app with SQLAlchemy as the ORM. I am trying to test a model with a class method:
# this is essentially a global used by all the models
Session = scoped_session(sessionmaker(autocommit=False))
class Role(Base):
__tablename__ = 'role'
id = sa.Column(sa.types.Integer, primary_key=True)
name = sa.Column(sa.types.Text, unique=True, nullable=False)
def __init__(self, **kwargs):
super(Role, self).__init__(**kwargs)
#classmethod
def find_all(self):
return Session.query(Role).order_by(Role.name).all()
I am using factory_boy to test and here is how I am trying to set up my testing factory:
import factory
from factory.alchemy import SQLAlchemyModelFactory
from sqlalchemy.orm import scoped_session, sessionmaker
from zk.model.meta import Base
from zk.model.role import Role
session = scoped_session(sessionmaker())
engine = create_engine('sqlite://')
session.configure(bind=engine)
Base.metadata.create_all(engine)
class RoleFactory(SQLAlchemyModelFactory):
FACTORY_FOR = Role
FACTORY_SESSION = session
However when I try to call RoleFactory.find_all() in a test, I get an error: E UnboundExecutionError: Could not locate a bind configured on mapper Mapper|Role|role, SQL expression or this Session
I tried monkeypatching meta and replacing that global Session with my session, but then I get this error: E AttributeError: type object 'RoleFactory' has no attribute 'find_all'
I tried calling RoleFactory.FACTORY_FOR.find_all() but then I get the same UnboundExecutionError.
Do I need to do something else for factory_boy to know about the class method?
This might be too obvious, but it seems that what you've got is a RoleFactory instance, when you need a Role instance, the factory wouldn't have access to any classmethods since it's not a child of the class. Try doing this and seeing what happens:
role = RoleFactory.build()
roles = role.find_all()

Categories