Trying to implement flask-security - python

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.

Related

How to fix 'flask db.create_all() error'?

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.

Flask, Blueprint Collisions and Pytest

I am using Flask Blueprints to create an application. I am testing it with pytest. Generating test_client in different states with pytest.fixture is causing a blueprint name collision.
This is my Flask __init__ code:
from flask import Flask
from flask_dropzone import Dropzone
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_user import UserManager
from flask_googlemaps import GoogleMaps
import os
from app.settings import app, db
# get extensions
dropzone = Dropzone()
google_maps = GoogleMaps()
migrate = Migrate()
def create_app(config_class=Config):
# initialise extensions
google_maps.init_app(app)
dropzone.init_app(app)
db.init_app(app)
migrate.init_app(app, db)
# FLASK-User Stuff
from app.models import User
user_manager = UserManager(app, db, User)
from app.errors import bp as errors_bp
app.register_blueprint(errors_bp)
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
I am then trying to use multiple pytests to test different aspects of the application. For each I am creating a pytest.fixture to generate a test_client. For example I'm creating two fixtures for testing the app with the db in different states.
For example.
#pytest.fixture(scope='module')
def client_state1():
app = create_app()
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' + PATH_TO_DB_STATE_1
app.config['TESTING'] = True
client = app.test_client()
ctx = app.app_context()
ctx.push()
yield client
ctx.pop()
#pytest.fixture(scope='module')
def client_state2():
app = create_app()
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' + PATH_TO_DB_STATE_2
app.config['TESTING'] = True
client = app.test_client()
ctx = app.app_context()
ctx.push()
yield client
ctx.pop()
def test_with_state1(client_state1):
"""Test should pass"""
assert 1 == 1
def test_with_state2(client_state2):
"""Test should pass"""
assert 1 == 1
When I run the tests I get the following error.
AssertionError: A blueprint's name collision occurred between <flask.blueprints.Blueprint object at 0x1a16b5d4e0> and <flask.blueprints.Blueprint object at 0x1a1633f6a0>. Both share the same name "googlemaps". Blueprints that are created on the fly need unique names.
I have tried splitting out tests into separate files and setting the scope of the fixture to function. But to no avail. In all cases the first test passes and the second causes the collision. What am I doing wrong?

Avoiding circular imports Python

I know this question have already been answered, but for my application, I can't resolve it. What I'm trying to do is to setup a database using MongoAlchemy instead of using MongoClient. I want to have diffrent scripts for every operation This is my main file, app.py:
import os
from flask import Flask, jsonify
from blueprints.db import insert_db
from blueprints.delete_blueprint import delete_bp
from blueprints.insert_blueprint import insert_bp
from blueprints.read_blueprint import read_bp
from blueprints.login_update_blueprint import log_update
import traceback
app = Flask(__name__)
app.register_blueprint(log_update)
app.register_blueprint(read_bp)
app.register_blueprint(insert_bp)
app.register_blueprint(delete_bp)
# database configuration
app.config['MONGOALCHEMY_DATABASE'] = 'rest_api'
# populate table with initial values
insert_db.populateTables()
# mail configuration
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'chis.simion12#gmail.com'
app.config['MAIL_PASSWORD'] = ''
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
#app.errorhandler(404)
def page_not_found(c):
return 'The required page does not exist'
#app.errorhandler(400)
def bad_req(c):
return 'Bad request. Please review the request'
#app.errorhandler(Exception)
def handle_Exception(error):
print("\n ______ ERROR ______\n")
print(traceback.format_exc())
print("_____________________\n")
response = dict()
response['message'] = "A PROBLEM HAS OCCURED, PLEASE TRY AGAIN LATER"
response['status_code'] = 500
response = jsonify(response)
response.status_code = 500
return response
if __name__ == '__main__':
app.secret_key = os.urandom(12)
app.run(debug=True, threaded=True)
Note that I'm modyfing on the old version, so the imports are still there. The problem with the circular imports appear when I try do import app from app.py into the script which initializes the database, described below:
from flask import current_app
from flask_mongoalchemy import MongoAlchemy
# from app import app
db = MongoAlchemy(current_app)
class People(db.Document):
Name = db.StringField()
Age = db.IntField()
Password = db.StringField()
Vms = db.AnythingField()
If I try to use current_app I get the error: RuntimeError: Working outside of application context. which means that the app is not currently initialized, and if I import the app from app.py the circular dependency appears. I'm sorry if I wasn't pretty clear, so feel free to ask for more details.
I like to place all extensions on a extensions.py file and create a function to initialize and configurate the app instance
extensions.py
from flask_mongoalchemy import MongoAlchemy
db = MongoAlchemy()
models.py
from extensions import db
class Person(db.Model):
pass
flaskr.py
from flask import Flask
from extensions import db
def create_app():
app = Flask(__name__)
db.init_app(app)
return app
app = create_app()
app.run()

Creating a database in flask sqlalchemy

I'm building a Flask app with Flask-SQLAlchemy and I'm trying to write a script that will create a Sqlite3 database without running the main application. In order to avoid circular references, I've initialized the main Flask app object and the SQLAlchemy database object in separate modules. I then import and combine them in a third file when running the app. This works fine when I'm running the app, as the database is built and operates properly when create rows and query them. However, when I try to import them in another module, I get the following error:
RuntimeError: application not registered on db instance and no applicationbound to current context
My code looks like the following:
root/create_database.py
from application.database import db
from application.server import app
db.init_app(app)
db.create_all()
root/run.sh
export FLASK_APP=application/server.py
flask run
root/application/init.py
from database import db
from server import app
db.init_app(app)
from routes import apply_routes
apply_routes(app)
root/application/database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
root/application/server.py
from flask import Flask
import os
app = Flask(__name__)
path = os.path.dirname( os.path.realpath(__file__) )
database_path = os.path.join(path, '../mydb.sqlite')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + database_path
root/application/models/init.py
from user import User
root/application/models/user.py
from application.database import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
password = db.Column(db.String(120))
def __init__(self, username, password):
self.username = username
self.password = password
In my create_database.py script I'm trying to make sure that the SQLAlchemy db instance is configured with the config details from the app object, but it doesn't seem to be connecting for some reason. Am I missing something important here?
You either have to create a request or you have to create the models with sqlalchemy directly. We do something similar at work and chose the former.
Flask lets you create a test request to initialize an app. Try something like
from application.database import db
from application.server import app
with app.test_request_context():
db.init_app(app)
db.create_all()

How do I remove a circular dependency between a Flask blueprint and the application initialisation? [duplicate]

I want to structure my Flask app something like:
./site.py
./apps/members/__init__.py
./apps/members/models.py
apps.members is a Flask Blueprint.
Now, in order to create the model classes I need to have a hold of the app, something like:
# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(current_app)
class Member(db.Model):
# fields here
pass
But if I try and import that model into my Blueprint app, I get the dreaded RuntimeError: working outside of request context. How can I get a hold of my app correctly here? Relative imports might work but they're pretty ugly and have their own context issues, e.g:
from ...site import app
# ValueError: Attempted relative import beyond toplevel package
The flask_sqlalchemy module does not have to be initialized with the app right away - you can do this instead:
# apps.members.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
# fields here
pass
And then in your application setup you can call init_app:
# apps.application.py
from flask import Flask
from apps.members.models import db
app = Flask(__name__)
# later on
db.init_app(app)
This way you can avoid cyclical imports.
This pattern does not necessitate the you place all of your models in one file. Simply import the db variable into each of your model modules.
Example
# apps.shared.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db
class Member(db.Model):
# TODO: Implement this.
pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member
def report_on_members():
# TODO: Actually use arguments
members = Member.filter(1==1).all()
return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members
reporting = Blueprint("reporting", __name__)
reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting
app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)
Note: this is a sketch of some of the power this gives you - there is obviously quite a bit more that you can do to make development even easier (using a create_app pattern, auto-registering blueprints in certain folders, etc.)
an original app.py: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
...
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
class Computer(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# Create the database tables.
db.create_all()
...
# start the flask loop
app.run()
I just splitted one app.py to app.py and model.py without using Blueprint. In that case, the above answer dosen't work. A line code is needed to work.
before:
db.init_app(app)
after:
db.app = app
db.init_app(app)
And, the following link is very useful.
http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

Categories