How to automatically create database tables and schema with Flask and Sqalchemy? - python

I have a small Flask application. I add an extra security layer which is log in. I based my refactoring on the DO article.
In a nutshell,
__init__.py:
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash
from sqlalchemy import create_engine
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'e56432f4402c9a0c166fe6c6a0a2fbbd'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
db.init_app(app)
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
return app
In order to create DB, I need to run in REPL:
from project import db, create_app
db.create_all(app=create_app())
This is both inconvenient and makes Docker image creation harder. I run an application as flask run. I saw similar issues but don't understand how to apply to my scenario.
How can I overcome this complication?
UPDATE 1:
My project structure is the following:
project/
project/
__init__.py
auth.py
main.py
models.py
static/
templates/
Dockerfile
requirements.txt

If you want the database to be created when you run the flask app:
I would put this code
def createMyDatabase():
from project import db, create_app
db.create_all(app=create_app())
into the file makedatabase.py and save it into your program's root directory. Then add from <path to makedatabase.py>/makedatabase import createMyDatabase to the top of your __init__.py file and then just after your import statements in __init__.py write createMyDatabase(). I think that this would be the easiest way to do it in your situation.
If you don't want the database to be created every time that you run the program:
You could just take the function out of the makedatabase.py file and then run the file just as it is before you run the flask application.

I found a more elegant solution
Instead of return app make the following block, so an application will be created in the runtime if and only if the context was initialized and database created.
with app.app_context():
db.create_all()
return app
Surprisingly the Flask-SqlAlchemy is pointing out to REPL db creation as a first solution, not the one above.
UPDATE:
Here is an example of my Dockerfile:
FROM python:3.7-slim
ENV FLASK_APP=.
WORKDIR /app
COPY requirements.txt /app/requirements.txt
COPY . /app
RUN pip install -r requirements.txt
CMD flask run -h 0.0.0.0 -p 5000
The catch is that you have to change how you run flask, not via python command.

Related

Having trouble getting Flask app running on cPanel using passenger_wsgi.py

I'm trying to get a Flask "point of sale" application working on cPanel with no success.
Here is the directory structure on cPanel File Manager
I have my app = Flask(name) in init.py
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_security import Security,SQLAlchemyUserDatastore
# from app.models import Role,User
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db, render_as_batch=True)
login = LoginManager(app)
from app import routes,models
I have tried using the line this line of code in my passenger_wsgi.py
from app import app as application
and in my pointofsale.py i used:
application = app
I have successfully created the python app and installed requirements.txt, when I try to load the link to the website but I cant reach the site.
Managed to figure out the issue, I had two files importing the application instance -: pointofsale.py and passenger_wsgi.py.
I deleted pointofsale.py, and added the following code to passenger_wsgi.py file
from app import app
application = app

Flask / Docker: Error: Could not import 'project.main.python'

I am trying to build and run a flask application using docker on GCP.
My file structure is as follows:
> /project
> static (contains some images)
> templates (contains html files)
> tmp_data (contains some data that is created during usage of the app)
> __init__.py
> auth.py (app has a login feature, this script helps authorize them)
> main.py
> Dockerfile
> some other function scripts
My Dockerfile is as follows:
FROM python:3.9-slim
RUN pip3 install Flask gunicorn scipy seaborn matplotlib pandas numpy flask_excel flask_login flask_sqlalchemy
COPY . /project
WORKDIR project
ENV FLASK_APP=main.python
# run flask in dev mode
ENV FLASK_ENV=development
EXPOSE 5001:5000
CMD ["flask", "run", "--host", "0.0.0.0"]
My main.py file is as follows:
from flask import Blueprint, render_template, redirect, url_for
from flask_login import login_required, current_user
from . import db
import flask_excel as excel
import json
# user written imports (not sure why it wants relative imports now?)
from .generate_data import get_truncated_normal, create_data
from .create_graphs import plot_graph, hist_plot, bar_plot, pie_plot, data_comments
from .generate_html_file import generate_html
main = Blueprint('main', __name__)
#main.route('/')
def index():
return render_template('index.html')
... some functions and flask views ...
When I build my app (successfully) using the command
~/some_upper_folder/project (my_sandbox)$ docker build -t project .
and then run it with the command
~/some_upper_folder/project (my_sandbox)$ docker run project
I get the error:
* Serving Flask app 'main.python' (lazy loading)
* Environment: development
* Debug mode: on
Usage: flask run [OPTIONS]
Try 'flask run --help' for help.
Error: Could not import 'project.main.python'.
I am not sure why this is occurring, I'm guessing this is due to the fact that I'm referencing an incorrect directory?
NOTE: The app is working correctly, I can run it using a venv created in another folder not seen here

Flask Run - ImportError was raised

Starting my Flask app using:
flask run
Doesn't appear to work... and I get the error message:
Error: While importing 'entry', an ImportError was raised.
however if I run:
python entry.py
The app will build successfully? Why is this? Both FLASK_APP and FLASK_ENV are set correctly, here is my folder structure:
entry.py:
from application import create_app
app = create_app()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
application/init.py:
import os
from flask import Flask
from config import DevConfig, TestConfig, ProdConfig
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
from application.models import Report
def create_app(testing=False):
app = Flask(__name__)
flask_env = os.getenv("FLASK_ENV", None)
# Configure the application, depending on the environment
if testing:
app.config.from_object(TestConfig)
elif flask_env == "development":
app.config.from_object(DevConfig)
elif flask_env == "production":
app.config.from_object(ProdConfig)
else:
raise ValueError("FLASK_ENV is not set or there is an unknown environment type!")
# init plugins, if any
db.init_app(app)
if flask_env == "development":
with app.app_context():
db.create_all()
db.session.commit()
# register blueprints
register_blueprints(app)
return app
def register_blueprints(app):
from application.main import main_blueprint
app.register_blueprint(main_blueprint)
If you want to start the application through the flask executable, then you have to consider that flask will look for the app.py file containing the app application, if not (as in your case), then you will have to correctly set the value of the environment variable FLASK_APP, which will be equal to FLASK_APP=entry.py:app
On Linux, macOS:
$ export FLASK_APP=entry.py:app
$ flask run
On Windows:
$ set FLASK_APP=entry.py:app
$ flask run
Take a look here: Run The Application.
Here, however, it is explained how flask looks for the application to run
In this case (python entry.py) everything works correctly, because flask is invoked via python inside the main section, which instead is not called if entry.py is executed directly from flask, in fact flask will not enter the main section, but will look for the app.py file and the app variable inside it. (Obviously it will look for entry.py and app if you have configured the environment variable correctly)

Flask Heroku Correct Procfile

I'm trying to use Heroku to push my Flask app to the web. I think I have everything down, except the Procfile. Here is what my file directory looks like:
My main.py is:
from application import app
My init.py is:
from flask import Flask
from config import Config
from flask_mongoengine import MongoEngine
app = Flask(__name__)
app.config.from_object(Config)
db = MongoEngine()
db.init_app(app)
from application import routes
What is the correct Procfile command here?
Thanks

How to setup Flask-Migrate with the correct database reference?

I am building a flask app and am trying to set up database migration using Flask-Migrate. I have gotten it working kind of, but have a weird issue of the app/migrate not looking in the same place for the .db file and need to know how to how to get them to look in the same place.
My app works perfectly and I am not getting any errors. The Flask-Migrate works perfectly and I am not getting errors. This is the simplified layout of my app currently:
app.py
app/
|- __init__.py
|- config.py
When I run 'flask run' it thinks the database is here:
app.py
app/
|- __init__.py
|- config.py
|- app.db <--(In the app folder)
But when I run 'flask db upgrade' it thinks that the database is here:
app.py
app.db <--(In the main folder)
app/
|- __init__.py
|- config.py
I have the SQLALCHEMY_DATABASE_URI set to 'sqlite:///app.db' and I can actually get them both to work properly if I constantly change the SQLALCHEMY_DATABASE_URI between 'sqlite:///app.db' and 'sqlite:///../app.db' between running the two commands.
app.py file
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
simplified __init__.py file:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.config import Config
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
migrate.init_app(app, db)
from app.routes import main
app.register_blueprint(main)
return app
simplified config.py file
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
I would expect flask run and flask db upgrade to see the .db file in the same place, but they do not. Instead they see them in the child folder or parent folder respectively.
The problem is that you are using a URL to your SQLite database, so any time the application changes the current working directory the location of your database will be calculated again relative to the new current directory.
The solution is simple, use an absolute URL. This is done when you set the SQLALCHEMY_DATABASE_URI environment variable, the value should be something like this:
export SQLALCHEMY_DATABASE_URI="sqlite:////home/yourname/yourapp/app.db"
Note that this is sqlite: followed by four slashes, not three. The first two slashes are the separator between the URL scheme and the rest of the URL. The 3rd slash separates the host/port from the path (host and port are empty on SQLite URLs). The 4th slash is the start of the absolute path to your database file.
I don't like absolute URIs.
in env.py:
config.set_main_option(
'sqlalchemy.url', current_app.config.get(
'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
(you would have to comment these lines to make it work)
basically uses what you use at SQLALCHEMY_DATABASE_URI.
in my case "sqlite:///db.sqlite" which would cause the problems outlined above, for the reasons outlined above (relative path issues)
To avoid the issue you describe, use a different (relative) uri for the env.py
(that is in the alembic.ini file, under[alembic] section):
[alembic]
sqlalchemy.url = sqlite:///app/db.sqlite
So now, you would have the correct -relative- path for all the different needs.
It worked for me, in this exact case.

Categories