I'm attempting to run flask migrate db in my working directory, and it does not use the model I defined in models.py
Here's the code.
models.py
import sys
sys.path.append("../")
from Talks2 import db
class Talk(db.Model):
presenter = db.Column(db.Text())
talkType = db.Column(db.Text())
desc = db.Column(db.Text(), primary_key=True)
link = db.Column(db.Text())
time = db.Column(db.Integer())
def __repr__(self):
return "Presenter: {}\nType: {}\nDescription:\n{}\nLink: {}".format(self.presenter,self.talkType,self.desc,self.link)
routes.py
import sys
sys.path.append("../")
from flask import Flask, request, render_template
from Talks2 import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app,db)
from Talks2 import models
#app.route("/")
def index():
return render_template("index.html")
#app.route("/add", methods=["POST"])
def add():
person = request.form["presenter"]
ttype = request.form["type"]
desc = request.form["desc"]
link = request.form["link"]
print(person, file=sys.stderr)
print(ttype, file=sys.stderr)
print(desc, file=sys.stderr)
print(link, file=sys.stderr)
return render_template("index.html")
if __name__ == "__main__":
app.run()
What do I need to change for it to correctly generate the script?
You are importing db from Talks2.py in models.py file and again in routes.py declaring again.
You haven't shared the code of Talks2.py file. What I am suspecting is you are declaring app and db object multiple times and replacing it with others.
Just do import in the proper way and your model will be detected by the flask.
The simplest solution is to declare app & db in Talks2.py, then import both in models.py and then from models.py import app & db in routes.py. This will resolve your problem.
Also, it should be flask db migrate instead of flask migrate db.
For more information refer to these commands:
To create a migration repository:
flask db init
To Generate Migration Script (Make sure to reviewed and edited, as Alembic currently does not detect every change you make to your models)
flask db migrate
To apply the migration to the database
flask db upgrade
To see all the commands that are available run this command:
flask db --help
For more info refer this official doc.
Let me know if this didn't help.
Related
I'm trying to develop a database driven flask app.
I have an api.py file which has the flask app, api and SQLAlchemy db objects and a users.py file which contains the routes ands code to create a database table.
In the users.py file, there's a UserManager Resource which has the routes. I have to add this resource to the API from this file.
So users.py needs to import the db and the api from the api.py file and api.py needs to serve the flask app so it has all the variables users.py needs, but api.py also needs to import users.py in order to use it as a flask blueprint.
api.py:
...
from user import user
app = Flask(__name__)
app.register_blueprint(user)
api = Api(app)
...
db = SQLAlchemy(app)
ma = Marshmallow(app)
...
if __name__ == '__main__':
app.run(debug=True)
users.py:
from api import api
user = Blueprint('user', __name__, template_folder='templates')
db = SQLAlchemy(api.app)
ma = Marshmallow(api.app)
class User(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(32), unique=True, nullable=False)
password = db.Column(db.String(32))
first_name = db.Column(db.String(32))
last_name = db.Column(db.String(32))
...
...
class UserManager(Resource):
#user.route('/get/<user_id>', methods = ['GET'])
def get_user(user_id):
...
This of course results in errors due to circular imports. Question is, how do I separate the flask files with the routes from the api file when the blueprint has dependencies from the api (the db and the api objects)
I also somehow have to do something like api.add_resource(UserManager, '/api/users') but I'm not sure where that'd go given the circular import.
Tried reducing dependencies between two files, but couldn't achieve described goal without doing a 2 way import.
Either trying to get 2 way import to work or still have same structure of separate files with routes but using 1 way import.
This is a well known problem in flask. The solution is to use application factories.
Cookiecutter Flask does this really well and offers a good template. It is well worth to check out their repo and try to understand what they are doing.
Assuming you have a folder app and this folder contains a file __init__.py and your other files user.py, etc.
Create a file app/extensions.py with this content and any other extension you need to initialize.
...
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
Import the db object (which was created, but not initialized) in the files where you need it.
from app.extensions import db
In your app/api.py file
from flask import Flask
from app.extensions import db
from app.user import user as user_bp
def create_app():
app = Flask(__name__)
register_extensions(app)
register_blueprints(app)
return app
def register_extensions(app):
"""Register Flask extensions."""
db.init_app(app)
return None
def register_blueprints(app):
"""Register Flask blueprints."""
app.register_blueprint(user_bp)
return None
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
Add stuff based on this approach as needed.
In development (so sqlite3) I'm getting this error on any database access:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: ujs ...
I got here by saying
export FLASK_ENV=development
export FLASK_APP=my_app.py
flask db init
flask db migrate
flask db upgrade
flask run
and then doing an HTTP GET against that dev server.
I believe the migration workflow succeeded, because when I use the sqlite3 commandline client, I can see the (empty) table with a believably correct schema.
╭╴ (get-db-working *%=)╶╮
╰ jeff#starshine:TN_flask_web $ sqlite3 dev.db
SQLite version 3.27.2 2019-02-25 16:06:06
Enter ".help" for usage hints.
sqlite> .table
alembic_version ujs
sqlite> .quit
╭╴ (get-db-working *%=)╶╮
╰ jeff#starshine:TN_flask_web $
I therefore believe I've made a coding error. But I'm not seeing it.
I have this code (pared down to what I believe is the essential bits):
my_app.py:
from app import create_app, db, cli
from app.models import UJS
app = create_app()
cli.register(app)
#app.shell_context_processor
def make_shell_context():
return {'db': db,
'UJS': UJS}
app/models.py:
from app import db
import time
def now_in_microseconds():
"""Return the current time in microseconds since the epoch.
"""
return time.time() * 1000 * 1000
class UJS(db.Model):
id = db.Column(db.Integer, primary_key=True)
timestamp_microseconds = db.Column(db.BigInteger, default=now_in_microseconds)
ip_hash = db.column(db.String(40))
# And then some more columns, all quite boring.
def __repr__(self):
return '<[{tag}]/[{ip}] {microsec}/{city}>'.format(
tag=self.tag, ip=self.ip_hash,
microsec=self.timestamp_microseconds, city=self.city)
app/__init__.py:
from flask import Flask, request, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
try:
app.config.from_pyfile("../config_local.py")
except FileNotFoundError:
print('No local config found.')
except:
print('Unexpected error on app.config.from_pyfile()')
db.init_app(app)
migrate.init_app(app, db)
...
return app
from app import models
and app/main/routes.py:
from flask import request, g, current_app, session
from app import db
from app.main import bp
from app.models import UJS
#bp.before_app_request
def before_request():
if 'static' == request.endpoint:
# This should only happen in dev. Otherwise, nginx handles static routes directly.
return
# I expect this to return an empty list, but it throws a 500.
print(UJS.query.all())
Any suggestions what I'm missing?
For anyone who might find this question later on: the problem was about having the right absolute path to your DB in your SQLALCHEMY_DATABASE_URI config value.
Also (this wasnt the case here, but it might possibly gotcha with the same symptoms) - if you omit __tablename__ on Model declaration, SQLAlchemy might autogenerate something you wont expect. Just a thing to keep in mind, if you're working with an existing DB with some schema already in place.
I got the below file structure for a Python-Flask app with flask-migrate :
My issues are
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in <module>
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
Here below is manage.py
# This file take care of the migrations
# in model.py we have our tables
import os
import unittest
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from app.main import create_app
from app.main import db
# # We import the tables into the migrate tool
from app.main.model import model
app = create_app(os.getenv('BOILERPLATE_ENV') or 'dev')
app.app_context().push()
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
#### If I add model.py here all should be easier , but still I have the
#### issue with
#### from app.main import create_app , db
#manager.command
def run():
app.run()
#manager.command
def test():
"""Runs the unit tests."""
tests = unittest.TestLoader().discover('app/test', pattern='test*.py')
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.wasSuccessful():
return 0
return 1
if __name__ == '__main__':
manager.run()
This is app.init.py where db and create_app are defined
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_cors import CORS
from .config import config_by_name
from flask_restful import Resource, Api
# from flask_restplus import Resource
from app.main.controller.api_controller import gconnect, \
showLogin, createNewTest, getTest, getTests, getIssue, createNewIssue
db = SQLAlchemy()
flask_bcrypt = Bcrypt()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_by_name[config_name])
cors = CORS(app,
supports_credentials=True,
resources={r"/api/*":
{"origins":
["http://localhost:3000",
"http://127.0.0.1:3000"]}})
api = Api(app)
db.init_app(app)
flask_bcrypt.init_app(app)
api.add_resource(gconnect, '/api/gconnect')
api.add_resource(showLogin, '/login')
api.add_resource(createNewTest, '/api/test')
api.add_resource(getTest, '/api/test/<int:test_id>')
api.add_resource(getTests, '/api/tests')
api.add_resource(getIssue, '/api/issue/<int:issue_id>')
api.add_resource(createNewIssue, '/api/issue')
return app
And this is (just one of the table for simplicity) of my model
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
# # # This will let sql alchemy know that these clasess
# # # are special Alchemy classes
# Base = declarative_base()
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
email = db.Column(db.String(250), nullable=False)
pictures = db.Column(db.String(250))
role = db.Column(db.String(25), nullable=True)
My issues are:
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
A simple solution is to create a seperate initializtions file besides your __init__.py. e.g. init.py where you initialize sqlalchemy along with other extensions. That way they can be imported in all the modules without any circular dependencies problems.
A more elegant solution however is to you use Flask's current_app and g proxies. They were made to help Flask users circumvent any problems with circular dependencies.
Typically you initalize the flask app in the __init__.py module and the __init__.py module sometimes has to import some variables from its sub-modules. This becomes problematic when sub-modules try to import initalized extensions
As a general rule of thumb, outer modules should be importing from their submodules not the other way around.
So here's one way you can solve your problem (cited from here):
** __init__.py
from flask import g
def get_db():
if 'db' not in g:
g.db = connect_to_database()
return g.db
#app.teardown_appcontext
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
Now you can easily import your db connection into any other module by:
from flask import g
db = g.db
db.do_something()
I am new at flask Blueprints, and I am getting the error AttributeError: 'function' object has no attribute 'query'. I am pretty sure that it is some thing simple that I am missing. I believe that my error is coming from the views.py file, and that db is not accessed, but I am not sure why. 'From project import db' should be doing this. I think.
Thank you for the help
Here is the code.
app.py
from project import app
if __name__ == 'main__':
app.run(debug=True)
__init__.py
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLALchemy
from project.ContentType.views import ContentType_BP
app.register_blueprint(ContentType_BP, url_prefix='/ContentType')
#app.route('/')
def index():
return render_template('index.html')
views.py
from flask import Blueprint, render_template
from project.ContentType.forms import ContentTypeForm
from project.models import ContentType
from project import db
ContentType_BP = Blueprint('ContentType',__name__,
template_folder = 'templates')
#ContentType_BP.route('/ContentType', methods=['GET','POST'])
def ContentType():
form = ContentTypeForm()
results = ContentType.query.all() ## this is were my error occurs
return render_template('contenttype/contenttype.html',
form=form, results=results)
Models.py
from project import db
class ContentType(db.Model):
id = db.Column(db.Integer, primary_key=True)
ct1 = db.Column(db.Integer)
ct2 = db.Column(db.Integer)
Below is the directory tree. I thought it might come in handy. '--' represents a sub-directory for Main
Main
app.py
__init__.py
--project
models.py
--ContentType
__init__.py
--static
--templates
--ContentType
contenttype.html
Looks like you are shadowing ContentType from your db model classes, since you are using it as the name of the view.
Try changing the name of the view:
def ContentType():
to something like:
def content_type():
and using that name instead.
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/