Flask + Peewee error AttributeError: 'Flask' object has no attribute 'compiler' - python-3.x

What am I doing?
I had been learning from a Flask tutorial (https://blog.miguelgrinberg.com/) and I'm stuck on the ORM section as it uses SQLAlchemy and I want to use Peewee (I use it in another project, and had been working nicely on it).
Noob Problematic
After configuring Peewee and define a BaseModel, and implementing it in a User model, I tried to do a query and got this error instead:
File "/home/atrevino/.local/share/virtualenvs/comparteme-qStpFUrM/lib/python3.6/site-packages/peewee.py", line 2932, in compiler
return self.database.compiler()
AttributeError: 'Flask' object has no attribute 'compiler'
Not sure if this is related to the configuration of my database, but I was able to get the MySQLDatabase object which makes me to think it's doing a connection to the DB properly.
The error above is displayed whenever I try to do something with Peewee's models, for example User.get(User.id == 1) or User.create(username='Name')
This is my test-app and environment configuration
./config.py
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-know'
DATABASE = {
'name': 'comparteme',
'engine': 'peewee.MySQLDatabase',
'user': 'root',
'password': 'whatever',
'host': os.environ.get('DATABASE_URL') or 'mysql://root#localhost:3306'
}
comparteme/__init__.py
import peewee
from flask import Flask
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
db = peewee.MySQLDatabase(app).database
from comparteme import routes
comparteme/models/base_model.py
import peewee
from comparteme import db
class BaseModel(peewee.Model):
class Meta:
database = db
class User(BaseModel):
username = peewee.CharField(unique=True)
I call all this from a flask route:
comparteme/routes.py
from comparteme import app
from comparteme.models.base_model import User
#app.route('/')
#app.route('/index')
def index():
User.create(username='Alan')
return 'returning any string'
To be able to test I have one test.py on my root directory that I added to FLASK_APP environment variable by using export FLASK_APP=test.py.
./test.py
from comparteme import app
After that I just do a regular flask run and open a browser with http://127.0.0.1:5000/ and get the error.
Is this the correct way to configure Peewee?
Thanks in advance !!!

Related

Flask-SQLAlchemy not creating tables due to import order?

I know there are a few threads out there with the same issue, but all of them have different ways they are initialising their flask applications, and the answers are sort of tailored to their setup. Some have put their SQLAlchemy init in their models files, some have just one massive application.py file with declarations at the top, some put it in __ init__.py.
But I've structured my flask applications the following way since I am comforable with this pattern:
init.py
| \ \ \ \
| \ \ \ \
app.py models.py init_db.py some_db_helper.py module_with_db_needs.py
Basically I have an init.py file that contains all the initialisation code for a Flask app:
init.py
from flask import Flask
from flask_wtf.csrf import CSRFProtect
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SECRET_KEY"] = "abcd"
db = SQLAlchemy()
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///testdata.sqlite3"
db.init_app(app)
csrf = CSRFProtect()
csrf.init_app(app)
From there, any other file that I need can just import the needed objects. So models.py can import db from init.py for defining models, app.py can import app for flask routes, and some_db_helper.py can also import db, etc:
models.py
from sqlalchemy import Table, ForeignKey, ...
from init import db, app # Import app, db from init file
class Category(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
# ... other properties
app.py
from init import app # Import app from init file
#app.route("/index.html", methods=["GET"])
def index():
return True
This file should create a new database file (I'm using sqlite), create the tables and data:
init_db.py
from models import * # Get app, db from models file
def insert_data():
# Insert some data into Category table
with app.app_context():
db.create_all()
insert_data()
However, when I run python init_db.py, my database file does get created, but it has no tables created at all. When the program tries to insert data, I get this error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: category
I am aware that the db models need to be defined first, so I have my from models import * line, then doing create_all() at the end after all is loaded. You can also see init_db.py is pulling db from the import *, so it's using the same db instance as the models.py file, which means it should be aware of the defined models before creating the tables.
How can I get my init_db.py to create the tables properly using my current folder structure / import strategy?
I have read your comments, you have built some anti patterns, so rather than shoehorning this into working, let's go back to a healthier hierarchy.
. root of your app
+-- app.py (initialise app and routes)
+-- models.py (create models)
+-- init_db.py (db initial data, if you really don't want it in models.py)
app.py
All app initialisation, can be run with flask --app path/to/app.py run.
from flask import Flask
from init_db import insert_initial_data
from models import db
app = Flask(__name__)
app.config["SECRET_KEY"] = "abcd"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///testdata.sqlite3"
db.init_app(app)
with app.app_context():
db.create_all()
insert_initial_data()
#app.route("/index.html", methods=["GET"])
def index():
return b"OK"
models.py
All DB schema.
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, Table
db = SQLAlchemy()
class Category(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
...
init_db.py
I'd argue that belongs in models.py if you need them in the database on init.
def insert_initial_data():
...
PS1. If you want to split out your routes I really recommend a blueprint, it as easy to use as the Flask instance in your app.
PS2. This is functional, it does run and creates the DB file.

AttributeError: module 'flask.app' has no attribute 'route'

I have autocomplete function as a separate file - autocomplete.py. The autocomplete is being imported into init.py as follows:
from xyzapp.autocomplete import autocomplete
The line that throws error in autocomplete.py is the following:
#app.route('/autocomplete',methods=['GET'])
The structure of the application is using blueprints and looks as follows:
appcontainer
|
|--run.py
|
|
|--xyzapp
|--__init__.py
|--autocomplete.py
|--blueprint_folder_1
|--blueprint_folder_2
|--static
The whole error message looks like this:
#app.route('/autocomplete',methods=['GET'])
AttributeError: module 'flask.app' has no attribute 'route'
Any ideas what I am doing wrong?
UPDATE:
The autocomplete.py looks as follows:
from flask import Flask, render_template, redirect, url_for, request, session, flash, app, Blueprint, jsonify
#app.route('/autocomplete',methods=['GET'])
def autocomplete():
database='backbone_test'
db=client[database]
all_names=list(db.ids.find({},{"current_name":1,"_id":0}))
return json.dumps(all_names)
The __init__.py file looks as follows:
from flask import Flask, render_template, Blueprint, jsonify, session
import jinja2
class MyApp(Flask):
def __init__(self):
Flask.__init__(self, __name__)
self.jinja_loader = jinja2.ChoiceLoader([self.jinja_loader,jinja2.PrefixLoader({}, delimiter = ".")])
def create_global_jinja_loader(self):
return self.jinja_loader
def register_blueprint(self, bp):
Flask.register_blueprint(self, bp)
self.jinja_loader.loaders[1].mapping[bp.name] = bp.jinja_loader
app = MyApp()
from xyzapp.autocomplete import autocomplete
from xyzapp.blueprint_folder_1.some_file import bp_1
app.register_blueprint(bp_1)
In autocomplete.py, it looks like you're importing app from Flask and not from your __init__.py file. Try importing from __init__.py instead.
import Flask first, the initiate your app
from flask import Flask
app = Flask(__name__)
I'm sorry this is a bit of a long shot, I'm not familiar with this way of running a Flask app, but it seems you create your own instance of the Flask class called MyApp and initialise it under the variable app.
I'm not 100% and may be completely wrong, but I think your problem lies in the __init__ of MyApp
Did you import Flask to your.py? Double check and make the addition of missing.

How do I properly set up a single SQLAlchemy session for each unit test?

When testing my Pyramid application using WebTest, I have not been able to create/use a separate Session in my tests without getting warnings about a scoped session already being present.
Here is the main() function of the Pyramid application, which is where the database is configured.
# __init__.py of Pyramid application
from pyramid_sqlalchemy import init_sqlalchemy
from sqlalchemy import create_engine
def main(global_config, **settings):
...
db_url = 'some-url'
engine = create_engine(db_url)
init_sqlalchemy(engine) # Warning thrown here.
Here is the test code.
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import init_sqlalchemy, Session
from sqlalchemy import create_engine
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Create session for tests.
db_url = 'same-url-as-above'
engine = create_engine(db_url)
init_sqlalchemy(engine)
# Note: I've tried both using pyramid_sqlalchemy approach here and
# creating a "plain, old" SQLAlchemy session here using sessionmaker.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user#email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
...
def tearDown(self):
self.base_tear_down()
def test_1(self):
# This is a typical workflow on my tests.
response = self.test_app.patch_json('/users/{0}'.format(self.user.id), {'email': 'new.email#email.com')
self.assertEqual(response.status_code, 200)
user = Session.query(User).filter_by(id=self.user.id).first()
self.assertEqual(user.email, 'new.email#email.com')
...
def test_8(self):
...
Running the tests gives me 8 passed, 7 warnings, where every test except the first one gives the following warning:
From Pyramid application: __init__.py -> main -> init_sqlalchemy(engine):
sqlalchemy.exc.SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created.
If this is of any use, I believe I am seeing the same issue here, except I am using pyramid_sqlalchemy rather than creating my own DBSession.
https://github.com/Pylons/webtest/issues/5
Answering my own question: I'm not sure if this is the best approach, but one that worked for me.
Instead of trying to create a separate session within my tests, I am instead using the pyramid_sqlalchemy Session factory, which is configured in the application. As far as I can tell, calls to Session in both test and application code return the same registered scoped_session.
My original intent with creating a separate session for my tests was to confirm that records were being written to the database, and not just updated in the active SQLAlchemy session. With this new approach, I've managed to avoid these "caching" issues by issuing Session.expire_all() at points in the tests where I transition between test transactions and application transactions.
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import Session
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Don't set up an additional session in the tests. Instead import
# and use pyramid_sqlalchemy.Session, which is set up in the application.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user#email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
Session.expire_all() # "Reset" your session.
def tearDown(self):
self.base_tear_down()

SQLAchemy 'No application found. Either work inside a view function or push'

Ello ello,
I found similar questions on the bug i'm facing, and tried the solutions offered but it didn't work for me.
I'm trying to separate out my models in a different directory and import them into the app.py
When I try to import the db into the python terminal, i'm getting the no application found.
app.py code
from flask import Flask
from flask_restful import Resource, Api
# from flask_sqlalchemy import SQLAlchemy
from routes import test, root, user
from models.todo import db
app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://username:pass123#localhost/db'
app.config['SECRET_KEY'] = 'thiskeyissecret'
# db.init_app(app)
with app.app_context():
api = Api(app)
db.init_app(app)
api.add_resource(root.HelloWorld, '/')
api.add_resource(test.Test, '/test')
api.add_resource(user.User, '/user')
if __name__ == '__main__':
app.run(debug=True)
models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Todo(db.Model):
__tablename__ = 'Todos'
id = db.Column('id', db.Integer, primary_key=True)
data = db.Column('data', db.Unicode)
def __init__(self, id, data):
self.id = id
self.data = data
def __repr__(self):
return '<Todo %>' % self.id
my file directory looks like
Main_app
Models
Todo.py
routes
some routes
app.py
Flask-SQLAlchemy needs an active application context.
Try:
with app.app_context():
print(Todo.query.count())
From the flask documentation:
Purpose of the Context
The Flask application object has attributes, such as config, that are
useful to access within views and CLI commands. However, importing the
app instance within the modules in your project is prone to circular
import issues. When using the app factory pattern or writing reusable
blueprints or extensions there won’t be an app instance to import at
all.
Flask solves this issue with the application context. Rather than
referring to an app directly, you use the the current_app proxy, which
points to the application handling the current activity.
Flask automatically pushes an application context when handling a
request. View functions, error handlers, and other functions that run
during a request will have access to current_app.
It is ok to have db initialised in app.py
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from routes import test, root, user
app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://username:pass123#localhost/db'
app.config['SECRET_KEY'] = 'thiskeyissecret'
db = SQLAlchemy(app)
api.add_resource(root.HelloWorld, '/')
api.add_resource(test.Test, '/test')
api.add_resource(user.User, '/user')
if __name__ == '__main__':
app.run(debug=True)
Then in your todo.py
from app import db
class Todo(db.Model):
__tablename__ = 'Todos'
id = db.Column('id', db.Integer, primary_key=True)
data = db.Column('data', db.Unicode)
def __init__(self, id, data):
self.id = id
self.data = data
def __repr__(self):
return '<Todo %>' % self.id
I get a same err
that err reason for just can operation db in viewfunc
def __init__(self, id, data):
self.id = id
self.data = data
try move that code operation to your viewfunc
In a nutshell, do something like this:
from yourapp import create_app
app = create_app()
app.app_context().push()

import error while using db = SQLAlchemy(app)

I get an Import error while importing the thermos.py module in my models.py module.
C:\Users\sys\Thermos\thermos\thermos.py
C:\Users\sys\Thermos\thermos\models.py
Here is the relevant part of the thermos.py module.
import os
from datetime import datetime
from flask import Flask, render_template, url_for, request, redirect, flash
from flask_sqlalchemy import SQLAlchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'thermos.db')
db = SQLAlchemy(app)
And, here is the relevant part of the models.py module.
from datetime import datetime
from thermos import db
Here is the image of the error I receive in CMD:
Kindly let me know what needs to be done to fix this issue.
Python can't decide if you're trying to import from the folder, or the file named thermos
You can rename
C:\Users\sys\Thermos\thermos\thermos.py
To
C:\Users\sys\Thermos\thermos\__init__.py
This makes the thermos directory a package with the db variable that can be used with an import
from . import db

Resources