Hello.
When i'm trying to create new record in model, i'm getting
AttributeError: 'dict' object has no attribute 'compiler'
Possible, that i'm getting this error because using Proxy class, but can't find mistake in my code.
init.py
from flask import Flask
from flask_peewee.db import Proxy
from flask_bootstrap import Bootstrap
from app.config import config_select
db = Proxy()
bootstrap = Bootstrap()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_select[config_name])
config_select[config_name].init_app(app)
db.initialize(config_select[config_name].DATABASE)
bootstrap.init_app(app)
#register blueprints
from app.blueprint.main import main_bp as main_blueprint
app.register_blueprint(main_blueprint)
return app
models.py
from peewee import *
from app import db
class BaseModel(Model):
class Meta:
database = db
class User(BaseModel):
id = PrimaryKeyField()
username = CharField(default='test', max_length=20)
class Meta:
db_table = 'users'
MODELS_LIST = [User]
Database was created by manager 'generate_db_tables' command
import os
from app import create_app
from flask_script import Manager, Shell
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
def connect_db():
from peewee import SqliteDatabase
db_lite = SqliteDatabase(app.config['DATABASE']['name'])
return db_lite
#manager.command
def generate_db_tables():
from app.models.models import MODELS_LIST
db_lite = connect_db()
for model in MODELS_LIST:
db_lite.create_table(model, safe=True)
print("Db tables created")
and the view function:
from flask import render_template, session, \
redirect, url_for, current_app, flash, request, make_response
from . import main_bp
from .forms import NameForm
from app.models.models import User
#main_bp.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
User.create(username=form.name.data) # <<<< Here is error
return url_for('.index')
return render_template('index.html', form=form)
Before i started to use Proxy class everything worked like a charm.
Seems, that i pass dict
DATABASE = {
'name': 'test.db',
'engine': 'peewee.SqliteDatabase'
}
to db.initialize()
instead of string name of database
DATABASE_INFO = 'test.db'
DATABASE = peewee.SqliteDatabase(DATABASE_INFO)
db.initialize(DATABASE)
Related
I'm building trying to learn Flask with a proof of concept Flask app, that takes a JSON payload, and uses SQLAlchemy to write it to a DB. I'm using celery to manage the write tasks.
The app is structured
|-app.py
|-project
|-__init__.py
|-celery_utils.py
|-config.py
|-users
|-__init_.py
|-models.py
|-tasks.py
app.py builds the flask app and celery instance.
app.py
from project import create_app, ext_celery
app = create_app()
celery = ext_celery.celery
#app.route("/")
def alive():
return "alive"
/project/__init__.py is the application factory for the flask app. It instantiates the extensions, links everything together, and registers the blueprints.
/project/init.py
import os
from flask import Flask
from flask_celeryext import FlaskCeleryExt
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from project.celery_utils import make_celery
from project.config import config
# instantiate extensions
db = SQLAlchemy()
migrate = Migrate()
ext_celery = FlaskCeleryExt(create_celery_app=make_celery)
def create_app(config_name=None):
if config_name is None:
config_name = os.environ.get("FLASK_CONFIG", "development")
# instantiate the app
app = Flask(__name__)
# set config
app.config.from_object(config[config_name])
# set up extensions
db.init_app(app)
migrate.init_app(app, db)
ext_celery.init_app(app)
# register blueprints
from project.users import users_blueprint
app.register_blueprint(users_blueprint)
# shell context for flask cli
#app.shell_context_processor
def ctx():
return {"app": app, "db": db}
return app
/project/celery_utils.py manages the creation of the celery instances
/project/celery_utils.py
from celery import current_app as current_celery_app
def make_celery(app):
celery = current_celery_app
celery.config_from_object(app.config, namespace="CELERY")
return celery
In the users dir, I'm trying to manage the creation of a basic user with celery task management.
'/project/users/init.py` is where I create the blueprints and routes.
/project/users/init.py
from flask import Blueprint, request, jsonify
from .tasks import divide, post_to_db
users_blueprint = Blueprint("users", __name__, url_prefix="/users", template_folder="templates")
from . import models, tasks
#users_blueprint.route('/users', methods=['POST'])
def users():
request_data = request.get_json()
task = post_to_db.delay(request_data)
response = {"id": task.task_id,
"status": task.status,
}
return jsonify(response)
#users_blueprint.route('/responses', methods=['GET'])
def responses():
request_data = request.get_json()
result = AsyncResult(id=request_data['id'])
response = result.get()
return jsonify(response)
/project/users/models.py is a simple User model - however, it does manage to successfully remain in the context of the flask app if created from the flask app cli.
/project/users/models.py
from project import db
class User(db.Model):
"""model for the user object"""
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(128), unique=True, nullable=False)
email = db.Column(db.String(128), unique=True, nullable=False)
def __init__(self, username, email, *args, **kwargs):
self.username = username
self.email = email
Finally, /project/users/tasks.py is where I handle the celery tasks for this dir.
/project/users/tasks.py
from celery import shared_task
from .models import User
from project import db
#shared_task()
def post_to_db(payload):
print("made it here")
user = User(**payload)
db.session.add(user)
db.session.commit()
db.session.close()
return True
The modules work, but as soon as I wire it all up and hit the endpoint with a JSON payload, I get the error message:
RuntimeError: No application found. Either work inside a view function or push an application context. ...
I have tried to preserve the app context in tasks.py by:
...
from project import db, ext_celery
#ext_celery.shared_task()
def post_to_db(payload):
...
...
from project import db, ext_celery
#ext_celery.task()
def post_to_db(payload):
...
These error with: TypeError: exceptions must derive from BaseException
I've tried pushing the app context
...
from project import db
from app import app
#shared_task()
def post_to_db(payload):
with app.app_context():
...
This also errors with: TypeError: exceptions must derive from BaseException
I've tried importing celery from the app itself
...
from project import db
from app import celery
#celery.task()
def post_to_db(payload):
...
This also errors with: TypeError: exceptions must derive from BaseException
Any suggestions gratefully received. There's a final piece of the puzzle I'm missing, and it's very frustrating.
With thanks to snakecharmerb
I had to add ContextTask to the make_celery() function in /project/celery_utils.py
from celery import current_app as current_celery_app
def make_celery(app):
celery = current_celery_app
celery.config_from_object(app.config, namespace="CELERY")
class ContextTask(celery.Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery.Task = ContextTask
return celery
And then a few tweaks in /project/users/tasks.py
from celery import shared_task
from .models import User
from project import db
#shared_task()
def post_to_db(payload):
user = User(**payload)
db.session.add(user)
db.session.commit()
db.session.close()
return True
Now I can see the user in the database, and my message queue is progressing as expected.
These are my different files, not all though. Everything works fine until the part where I submit a form. I suspect it has something to do with the linking of the database if that makes sense?
#init.py
import os
from os import path
from venv import create
from flask import Flask, render_template, flash, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from auth import auth
from models import db
from main import app
basedir = os.path.abspath(os.path.dirname(__file__))
DB_NAME = "bookings.sqlite"
app.config\['SECRET_KEY'\] = 'Thisissupposedtobesecret!'
app.config\['SQLALCHEMY_DATABASE_URI'\] = 'sqlite:///' + os.path.join(basedir, DB_NAME)
app.config\['SQLALCHEMY_TRACK_MODIFICATIONS'\] = False
app.register_blueprint(auth, url_prefix="/")
db.init_app(app)
def create_database(app):
if not path.exists('V3/' + DB_NAME ):
db.create_all(app=app)
create_database(app)
if __name__ == '__main__':
app.run(debug=True)
#forms.py
from flask_wtf import FlaskForm
from wtforms import (StringField, BooleanField, DateTimeField,
RadioField,SelectField,
TextAreaField,SubmitField)
from wtforms.validators import DataRequired
class SignUpForm(FlaskForm):
name = StringField("Your FULL Name", validators=\[DataRequired()\])
(form questions)
submit = SubmitField("Book")
#models.py
from main import app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy(app)
Migrate(app,db)
class Booking(db.Model):
__tablename__ = "bookings"
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.Text)
(other colums)
def __init__(self,name):
self.name = name
Tried to submit form which was successful but was not updated in the database
You need to push a context so you can access the database
https://flask.palletsprojects.com/en/2.0.x/appcontext/
def create_database(app):
if not path.exists('V3/' + DB_NAME ):
with app.app_context():
db.create_all()
Under models.py added
def __init__(self,name,x,y,z):
self.name = name
self.x = x
self.y = y
self.z = z
Under auth.py added
if request.method == 'POST':
name = form.name.data
x = form.x.data
y = form.y.data
z = form.z.data
new_booking = Booking(name=name,x=x,y=y,z=z)
db.session.add(new_booking)
db.session.commit()
return redirect(url_for('auth.bookings'))
else:
return render_template('bookings.html', form=form)
this has solved the issue, although i do not exactly understand why but it solved it
I'm have flask init with create app function. I create test file for my unitests. In unitest class add setUp function were i create new flask app and add context, push context to it. Next i create test db with create_all() function and, where i start test file, i have next error:
in _execute_for_all_tables op(bind=self.get_engine(app, bind), **extra)
TypeError: create_all() got an unexpected keyword argument 'forms'
I haven't 'forms' files or variable on models or anywere.
Before this error place in flask_sqlalchemy/init.py MetaData(bind=None), may be it some help.
from flask import Flask
from config import Config
from blocker_application.database import db
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_moment import Moment
migrate = Migrate()
login = LoginManager()
login.login_view = 'user.login'
moment = Moment()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
moment.init_app(app)
from blocker_application.main.routes import bp_main
from blocker_application.errors.handlers import bp_error
from blocker_application.reports.routes import bp_reports
from blocker_application.user.routes import bp_user
from blocker_application.applications.routes import bp_applications
app.register_blueprint(bp_main)
app.register_blueprint(bp_error)
app.register_blueprint(bp_reports, url_prefix='/reports')
app.register_blueprint(bp_user, url_prefix='/user')
app.register_blueprint(bp_applications, url_prefix='/applications')
return app
from blocker_application import models
________________________________________________________________________
/config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'not realy finish secret key configuration'
SQLALCHEMY_DATABASE_URI = 'mysql://some_database'
SQLALCHEMY_TRACK_MODIFICATIONS = False
class TestConfig(Config):
TESTING = True
SQLALCHEMY_BINDS = {'test': 'mysql://some_database_test'}
______________________________________________________________________
/tests.py
import unittest
from blocker_application import create_app, db
from blocker_application import models
from config import TestConfig
class UserModelCase(unittest.TestCase):
def setUp(self):
self.app = create_app(TestConfig)
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all(bind='test')
def tearDown(self):
db.session.remove()
db.drop_all(bind='test')
self.app_context.pop()
def test_password(self):
u = models.User(username='Mark')
u.set_password('Mark')
self.assertTrue(u.check_password('Mark'))
self.assertFalse(u.check_password('Tony'))
if __name__ == '__main__':
unittest.main(verbosity=2)
I found decision. Unittest work ok, after recreate the virtual enveroment.
I am trying to create a skeleton and I am using flask security for the login/admin.
Inside my app folder I have a function where I create my app:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore
from .views.home import home_blueprint
from .views.contact import contact_blueprint
from .views.login import login_blueprint, bcrypt, login_manager
from .models import db
from .models.user import User, Role
def create_app():
app = Flask(__name__, instance_relative_config=True)
db.init_app(app)
bcrypt.init_app(app)
login_manager.init_app(app)
app.register_blueprint(home_blueprint)
app.register_blueprint(contact_blueprint)
app.register_blueprint(login_blueprint)
app.config.from_object('config')
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
return app
Now the issue comes up when I try to create a dummy admin user to test.
For this I have a create_db.py file in the skeleton root folder.
from app import create_app
from sqlalchemy import exists
from flask_security.utils import encrypt_password
from app.models import db
from app.models.user import Role, User
create_app().app_context().push()
db.create_all()
user_role = Role(name='user')
db.session.add(user_role)
db.session.commit()
super_user_role = Role(name='superuser')
db.session.add(super_user_role)
db.session.commit()
user_role = Role(name='user')
super_user_role = Role(name='superuser')
admin_user = user_datastore.create_user(
username='admin',
email='admin',
password=encrypt_password('admin'),
confirmed_at=datetime.datetime.now(),
roles=[user_role, super_user_role]
)
db.session.commit()
When I run this I get NameError: user_datastore not define?
For story purpose, I implemented flask-login first and was able to get create a database.
In file create_db.py the variable user_datastore doesn't exist hence the error. You could do something like the following in create_dy.py (untested):
app = create_app()
app.app_context().push()
# get the app security instance
security = app.extensions.get('security')
# or like this
# security = app.extensions['security']
# ... etc
# Use the security api to create users
admin_user = security.datastore.create_user(
username='admin',
email='admin',
password=encrypt_password('admin'),
confirmed_at=datetime.datetime.now(),
roles=[user_role, super_user_role]
)
# ... etc
Also I'm not sure you need to login_manager.init_app(app) in your create_app() function.
I am using Flask to build an experimental application using Blueprint. Here is my project's structure:
-myproject/
requirements.txt
run.py
-apps/
-static/
-template/
-database/
__init__.py
views.py
model.py
auth.py
message.py
I init my app in init.py, using blueprint to integrate other parts of the app.
app = Flask(__name__)
from .views import views
from .model import model, db
from .auth import auth, login_manager
db.init_app(app)
app.register_blueprint(views)
app.register_blueprint(model)
app.register_blueprint(auth)
Then in views.py:
from flask import Flask, request, redirect, render_template, session, g, url_for, Blueprint
views = Blueprint('views', __name__)
from sqlalchemy.orm import scoped_session, relation, sessionmaker
# ORM Session
orm_session = scoped_session(sessionmaker())
#views.route('/courses')
def courses():
courses = orm_session.query(Course).order_by('-id')
return render_template('courses.html', courses=courses)
My Course class is defined in model.py:
class Course(db.Model):
__tablename__ = 'course'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
subjects = db.relationship('Subject', secondary='course_subject_link')
students = db.relationship('Student', secondary='course_student_link')
active = db.Column(db.Boolean)
def __repr__(self):
return "<{0}:{1.name}:{1.subjects!r}:{1.students!r}>".format(Course, self)
In template folder, I have put something like {% for course in courses %} and {% set active_page = "courses" %} in the template file. When I run the app, it gives me this error:
UnboundExecutionError: Could not locate a bind configured on mapper Mapper|Course|course, SQL expression or this Session
I did not use Blueprint before so the app used to be able to run. But after I used Blueprint the urls seem to be broken. How can I fix this?
I know what the problem is.
From the hint in the error message I guess that I forget to create_engine and pass the engine to sessionmaker using bind=engine.
Here is the code I changed in views.py:
from os.path import join, dirname
_cwd = dirname(__file__)
engine = create_engine('sqlite:///' + join(_cwd, 'database/courses.sqlite'))
Session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
orm_session = Session()
Here database/courses.sqlite is the location of my database file.