Seperate Database for flask admin - flask

can anyone tell me, how to make a separate Database just for Flask admin and maybe flask security?
this is how i am working with my PostgreSQL database just to load some tables and perform CRUD:
import flask_admin
from flask import Flask
from sqlalchemy import create_engine
app =Flask(__name__)
engine = create_engine{'postgresql://name:password#localhost/tablesdatabase')
i wish to make a separate database for flask-admin :( this is what i am trying)
admin = flask_admin.Admin(app)
app.config['SQLALCHEMY_BINDS'] = {'admindb' : 'postgresql://name:password#localhost/adminedatabase'}
admin.add_view(ModelView) ? // how can i impelement this ? with a seperate datbase ?

There is no official support, but you can customize Flask-SQLalchemy session to use different connections, here the example for using master slave connections, you can easily add as many connections as you want
from functools import partial
from sqlalchemy import orm
from flask import current_app
from flask_sqlalchemy import SQLAlchemy, get_state
class RoutingSession(orm.Session):
def __init__(self, db, autocommit=False, autoflush=True, **options):
self.app = db.get_app()
self.db = db
self._bind_name = None
orm.Session.__init__(
self, autocommit=autocommit, autoflush=autoflush,
bind=db.engine,
binds=db.get_binds(self.app),
**options,
)
def get_bind(self, mapper=None, clause=None):
try:
state = get_state(self.app)
except (AssertionError, AttributeError, TypeError) as err:
current_app.logger.info(
'cant get configuration. default bind. Error:' + err)
return orm.Session.get_bind(self, mapper, clause)
# If there are no binds configured, use default SQLALCHEMY_DATABASE_URI
if not state or not self.app.config['SQLALCHEMY_BINDS']:
return orm.Session.get_bind(self, mapper, clause)
# if want to user exact bind
if self._bind_name:
return state.db.get_engine(self.app, bind=self._bind_name)
else:
# if no bind is used connect to default
return orm.Session.get_bind(self, mapper, clause)
def using_bind(self, name):
bind_session = RoutingSession(self.db)
vars(bind_session).update(vars(self))
bind_session._bind_name = name
return bind_session
class RouteSQLAlchemy(SQLAlchemy):
def __init__(self, *args, **kwargs):
SQLAlchemy.__init__(self, *args, **kwargs)
self.session.using_bind = lambda s: self.session().using_bind(s)
def create_scoped_session(self, options=None):
if options is None:
options = {}
scopefunc = options.pop('scopefunc', None)
return orm.scoped_session(
partial(RoutingSession, self, **options),
scopefunc=scopefunc,
)
Than the default session will be master, when you want to select from slave you can call it directly, here the examples:
In your app:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///master'
app.config['SQLALCHEMY_BINDS'] = {
'slave': 'postgresql:///slave'
}
db = RouteSQLAlchemy(app)
Select from master
session.query(User).filter_by(id=1).first()
Select from slave
session.using_bind('slave').query(User).filter_by(id=1).first()

Related

Transaction rollback and dropping database in pytest fixtures

I'm writing functional tests to test API endpoints with pytest.
I want to achieve:
that at the end of each test DB transaction rolls back so that each test starts with the same data.
at the end of the test session, I want to delete the testing DB.
Bellow is my conftest.py file, and what is currently happening is that transactions aren't rolled back, and when it comes to the end of the session, it keeps hanging at drop_database on teardown, while it doesn't hang at the beginning when checking if DB exists (marked also in comments).
What do I need to do to make those rollbacks? And is there anything else I need to close, in order for that drop_database to take place?
Thank you!
import pytest
import sqlalchemy
from sqlalchemy_utils import database_exists, create_database, drop_database
from alembic.command import upgrade
from alembic.config import Config
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from api.views import dtm_api, diagrams_api, labels_api
from .data.dummy_data import import_dummy
ALEMBIC_CONFIG = 'migrations/alembic.ini'
#pytest.fixture(scope='session')
def app(request):
_app = Flask(__name__)
_app.config.from_object('api.config.TestingConfig')
_app.register_blueprint(diagrams_api.bp)
_app.register_blueprint(dtm_api.bp)
_app.register_blueprint(labels_api.bp)
ctx = _app.app_context()
ctx.push()
def teardown():
ctx.pop()
request.addfinalizer(teardown)
yield _app
#pytest.fixture(scope='session')
def test_client(app, request):
yield app.test_client()
#pytest.yield_fixture(scope='session')
def db(app, request):
engine = sqlalchemy.create_engine(app.config.get("SQLALCHEMY_DATABASE_URI"))
if database_exists(engine.url):
drop_database(engine.url) #here it doesn't hang
create_database(engine.url)
_db = SQLAlchemy()
_db.init_app(app)
_db.app = app
#Make migrations and add dummy data
Migrate(app, _db)
config = Config(ALEMBIC_CONFIG)
config.set_main_option("script_location", "migrations")
with app.app_context():
upgrade(config, 'head')
import_dummy(_db)
def teardown():
drop_database(engine.url) #here it hangs
engine.dispose()
request.addfinalizer(teardown)
yield _db
#pytest.fixture(scope='function', autouse=True)
def session(db, request):
connection = db.engine.connect()
options = dict(bind=connection, binds={}, autoflush=False, autocommit=False)
db.session = db.create_scoped_session(options=options)
def teardown():
db.session.rollback()
db.session.close()
connection.close()
request.addfinalizer(teardown)
yield db.session

How to make a sqlalchemy database session per request in Flask?

I need to ensure transactionality with flask requests. Is there a way to store a database session per request and use that one?
The easiest way is to create a simple extension for session initialization and commit changes after successful request. Here is an example:
from datetime import datetime
from flask import Flask, current_app, _app_ctx_stack
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
from sqlalchemy import Column, String, Integer
# Base model without connection(see: DBExtension.connect()) and a few demo models
Base = declarative_base(cls=DeferredReflection)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String())
class Log(Base):
__tablename__ = 'log'
id = Column(Integer, primary_key=True, autoincrement=True)
text = Column(String())
# extension for getting db session
class DBExtension:
def __init__(self, app: Flask = None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
app.teardown_appcontext(self.teardown)
def connect(self):
# init engine and set into metadata
engine = create_engine(current_app.config['DB_CONNECTION'], echo=True)
Session = sessionmaker(bind=engine)
session = Session()
# create_all - just for demo(init structure...)
Base.metadata.create_all(engine)
Base.metadata.bind = engine
Base.prepare(engine)
return session
def teardown(self, exception):
ctx = _app_ctx_stack.top
if hasattr(ctx, 'session'):
ctx.session.close()
#property
def session(self):
# connect or get active connection
ctx = _app_ctx_stack.top
if ctx is not None:
if not hasattr(ctx, 'session'):
ctx.session = self.connect()
return ctx.session
app = Flask(__name__)
app.config.update({'DB_CONNECTION': 'sqlite:///test.db'}) # sqlite for demo
db = DBExtension(app)
#app.teardown_request
def teardown_request_func(error=None):
# request processing completed(before requests functions, endpoints and other logic)
if not error:
# one more record and commit all changes in one transaction
db.session.add(Log(text='{dt}. teardown_request'.format(dt=datetime.utcnow())))
try:
db.session.commit()
except Exception as e:
print(e)
# do something
#app.before_request
def before_request_func():
# add a new record before request
db.session.add(Log(text='{dt}. before request'.format(dt=datetime.utcnow())))
#app.route('/user/<name>')
def user_route(name: str):
# do something with db session
db.session.add(Log(text='saving user {user}'.format(user=name)))
user = User(name=name)
db.session.add(user)
# session.add(newModel) or session.delete(oneMoreModel) etc.
return 'done' # return without commit. see: teardown_request_func
if __name__ == '__main__':
app.run(host='localhost', port=5000, debug=True)
Run server, open http://localhost:5000/user/DavidBoshton and see logs(BEGIN-...-COMMIT).

how to create flask sqlalchemy database uri using user input form and passed to the flask application config object

I am working on flask app and trying to create user input form through which user can connect to their database.I just want to provide functionally to the users so that they can make a connection from flask application to the database using user interface instead of providing all configuration values in config.py file.
I have written view functions to render flask wtform and accepting input from form and creating sqlalchemy database uri indside a view function.I am not able create sqlalchemy database uri as a globally so that it can be pass to flask application object.
__ init__.py
from flask import Flask,render_template,request
from flask_bootstrap import Bootstrap
from app.forms import ConnectDB
def create_app():
app = Flask(__name__)
Bootstrap(app)
app.config['SECRET_KEY'] = "123456789"
#app.route('/')
def index():
form = ConnectDB()
return render_template('index.html', form=form )
#app.route('/connect/' , methods=['GET','POST'])
def connect():
form = ConnectDB()
if request.method == 'POST' and form.validate():
database = form.database.data
username = form.username.data
password = form.password.data
host = form.host.data
port = form.port.data
database_name = form.database_name.data
uri = "{DATABASE_DIALECT}://{DB_USER}:{DB_PASSSWORD}#{DB_ADDRESS}:
{DB_PORT}/{DATABASE_NAME}".format(DATABASE_DIALECT =database,
DB_USER = username,DB_PASSSWORD = password,DB_ADDRESS =host,
DB_PORT = port,DATABASE_NAME = database_name)
config['SQLALCHEMY_DATABASE_URI'] = uri
from app.models import db
db.init_app(app)
# Blueprints
from app.views import geoit
app.register_blueprint(geoit)
return app
I know SQLALCHEMY_DATABASE_URI became local to connect view function and can not access outside of view.
But I don't know other possible way so that i can load database configurations in the flask app using user input.
Please help out for this problem.

Minimial test of database connectivity in django

I'm writing a quick little test view that I can GET every few minutes to check that my application and database servers are online and functioning.
from django.http import HttpResponse, HttpResponseServerError
from my_app.models import Model
def test(request):
'''
Returns a HttpResponse to prove the application/db servers work
'''
if request.method == "GET":
#test database access
count = Model.objects.values_list('id', flat=True).count()
if count:
return HttpResponse("OK")
return HttpResponseServerError("Error")
Is there a more minimal inbuilt database query that I could do to test that the db server is working and takling to the app server, without having to do a fake one like I did above?
You can import django connections and check with your databases:
from django.db import connections
try:
cursor = connections['default'].cursor()
except OperationalError:
return HttpResponseServerError("Error")
else:
return HttpResponse("OK")
return HttpResponseServerError("Error")
Documentation here

How to prevent double data submission in flask-sqlalchemy

I'm studying now main concepts of flask and flask-sqlalchemy. Keeping in my mind info from the tutorial (intro and contexts) I'm trying to create a simple database.
My Flask app is strucured as follows:
./db/
./db/models.py
./main.py
The contents of the files is as follows:
./main.py:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object(__name__)
from db.models import create_app
dbapp = create_app()
with dbapp.test_request_context():
from db.models import db, mumsUsers
db.create_all()
db.session.rollback()
admin = mumsUsers("admin", "admin#example.com")
db.session.add(admin)
db.session.commit()
./db/models.py:
from flask.ext.sqlalchemy import SQLAlchemy
from flask import Flask, current_app
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db.init_app(app)
return(app)
class mumsUsers(db.Model):
__tablename__ = 'mumsUsers'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(80), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
When I check the sqlite database, I see that sqlalchemy is trying to send commit() twice. So I have to remove the unique=True parameter in order to stop application crash. Meantime when I run the following commans from the python shell:
admin = mumsUsers('admin', 'admin#example.com')
db.session.add(admin)
db.session.commit()
only one record appears (as it is expected).
Therefore my question is how to prevent double call for commit()?
Update
The appeared problem has been caused by my fault, while making looped imports. Indeed I didn't notice I called import for the application package.
Therefore please ignore this post.
The caused problem has been related to looped imports.
Please check what you import before asking.