how to give structure to python flask application - python

I am new to python flask
Experimenting some end points with MongoDB as shown below in a single file
from flask import Flask, request
from flask.ext.mongoalchemy import MongoAlchemy
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['MONGOALCHEMY_DATABASE'] = 'library'
db = MongoAlchemy(app)
class Author(db.Document):
name = db.StringField()
class Book(db.Document):
title = db.StringField()
author = db.DocumentField(Author)
year = db.IntField();
#app.route('/author/new')
def new_author():
"""Creates a new author by a giving name (via GET parameter)
e.g.: GET /author/new?name=Francisco creates a author named Francisco
"""
author = Author(name=request.args.get('name', ''))
author.save()
return 'Saved :)'
#app.route('/authors/')
def list_authors():
"""List all authors.
e.g.: GET /authors"""
authors = Author.query.all()
content = '<p>Authors:</p>'
for author in authors:
content += '<p>%s</p>' % author.name
return content
if __name__ == '__main__':
app.run()
Above code which contains two end points to post and get the data which is working fine
Know looking for a way to separate the code into different file like
the database connection related code should be in different file
from flask import Flask, request
from flask.ext.mongoalchemy import MongoAlchemy
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['MONGOALCHEMY_DATABASE'] = 'library'
db = MongoAlchemy(app)
I should be able to get the DB reference in different files where the schema is define and use it
class Author(db.Document):
name = db.StringField()
class Book(db.Document):
title = db.StringField()
author = db.DocumentField(Author)
year = db.IntField();
and routes will be different file
#app.route('/author/new')
def new_author():
"""Creates a new author by a giving name (via GET parameter)
e.g.: GET /author/new?name=Francisco creates a author named Francisco
"""
author = Author(name=request.args.get('name', ''))
author.save()
return 'Saved :)'
#app.route('/authors/')
def list_authors():
"""List all authors.
e.g.: GET /authors"""
authors = Author.query.all()
content = '<p>Authors:</p>'
for author in authors:
content += '<p>%s</p>' % author.name
return content
Here in the endpoints file i should get the reference of database schema please help me in getting this structure
Point me to some understandable sample or video which can help me to do,I am new to python as well as flask please point some sample and help to learn more thanks

A basic structure could look like this:
/yourapp
/run.py
/config.py
/yourapp
/__init__.py
/views.py
/models.py
/static/
/main.css
/templates/
/base.html
/requirements.txt
/venv
Applied to your example it would look like this.
run.py: Start your application.
from yourapp import create_app
app = create_app()
if __name__ == '__main__':
app.run()
config.py: Contains configuration, you could add subclasses to differentiate between Development config, Test config and Production config
class Config:
DEBUG = True
MONGOALCHEMY_DATABASE = 'library'
yourapp/_init_.py: Initialization of your application creating a Flask instance. (Also makes your app a package).
from flask import Flask
from flask.ext.mongoalchemy import MongoAlchemy
from config import Config
db = MongoAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
from views import author_bp
app.register_blueprint(author_bp)
return app
yourapp/models.py: Contains your different models.
from . import db
class Author(db.Document):
name = db.StringField()
class Book(db.Document):
title = db.StringField()
author = db.DocumentField(Author)
year = db.IntField();
yourapp/views.py: Also called the routes.py sometimes. contains your url endpoints and the associated behavior.
from flask import Blueprint
from .models import Author
author_bp = Blueprint('author', __name__)
#author_bp.route('/author/new')
def new_author():
"""Creates a new author by a giving name (via GET parameter)
e.g.: GET /author/new?name=Francisco creates a author named Francisco
"""
author = Author(name=request.args.get('name', ''))
author.save()
return 'Saved :)'
#author_bp.route('/authors/')
def list_authors():
"""List all authors.
e.g.: GET /authors"""
authors = Author.query.all()
content = '<p>Authors:</p>'
for author in authors:
content += '<p>%s</p>' % author.name
return content
yourapp/static/... Contains your static files.
yourapp/templates/.. Contains your templates.
requirements.txt has a snapshot of your package dependencies.
venv (Virtualenv) folder where your python libs are to be able to work in a contained environment.
References:
Have a look at this related question.
Good example of a widely used project structure.

Related

How to separate flask files when two files depend on each other?

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.

Fixing internal server error heroku/flask

I have made my first heroku web app; it is a page with several links that redirect you to random pages just to test their ability to work. All the pages work apart from the link that views all the entries created in my postgres database. Every time I try to load that page I get an internal server error.
I have created my DATABASE_URL and set my PYTHONPATH all through my heroku cli through bash; and also set my procfile & wsgi file. I have tried changing things around a little but it rather crashes the whole app rather than fixing my bug.
here is app.py file.
import os
import pyscopg2
from flask import Flask, render_template, g, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://fikwczdiymxhwf:73bf42c2c8a15fa59b77e93654b6383e1cf4f85bdf0156818d1cf39a77815f13#ec2-54-243-47-196.compute-1.amazonaws.com:5432/d3uburco4fea1b'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
class Example(db.Model):
__tablename__ = "example"
id = db.Column(db.Integer, primary_key=True)
info = db.Column(db.String, )
name = db.Column(db.String, )
city = db.Column(db.String, )
def __repr__(self):
return '<Example {}>'.format(self.info)
DATABASE_URL = os.environ.get('postgres://fikwczdiymxhwf:73bf42c2c8a15fa59b77e93654b6383e1cf4f85bdf0156818d1cf39a77815f13#ec2-54-243-47-196.compute-1.amazonaws.com:5432/d3uburco4fea1b')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/page_2')
def page_2():
return render_template('random_page_2.html')
#app.route('/hello')
def hello():
return render_template('hello.html')
#app.route('/view_database')
def view_db():
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
data = cur.execute("SELECT * FROM example")
cur.close()
conn.close()
return render_template('view_database.html', data=data.fetchall())
main.py
import os
from app import app
if __name__ == '__main__':
app.debug = True
app.run()
wsgi.py
from whitenoise import WhiteNoise
from app import app
application = WhiteNoise(app)
application.add_files('static/', prefix='static/')
These three files are are within one folder - along with the templates & static folder for pages, pictures stylesheet; this folder is labled flask-sqlalchemy-test-02.
Outside this folder there are the following files: procfile, requirements.txt, schema.sql, runtime
procfile:
web:gunicorn flask-sqlalchemy-test-02.wsgi:application --log-file=-
runtime:
pyhton-3.7.3
requirements.txt:
To obtain this, I went to my development folder in my command line directory and typed:
pip freeze > requirements.txt
I have been looking at other heroku deployed apps online and notice they have a gitignore file and some have an venv (environment folder). I don't know if I have to include this in my app myself in order for this to work the way I intended. I'm pretty sure its probably a missing / wrong line of code, but I really don't know where to look and would be very grateful if anybody could help!!
This is my major hurdle i need to get over in order to start making more interactive webpages.
web app:
https://flask-sqlalchemy-test-02.herokuapp.com/
my GitHub repository:
https://github.com/SinclairPythonAkoto/flask-sqlalchemy-test-02
you test the app yourself and check on the full details of my code.
many thanks! :)

Flask objects on multiple sessions when they not should be

I have a similar problem to the user here: SQLAlchemy Object already attached to session
And I'm getting basically the same error:
'<Link at 0x7f31a785f630>' is already attached to session '1' (this is '15')
I'm really trying to figure out why multiple sessions are being created, when I only want one. I have two files __init__.py and models.py:
Lines of interest from __init__.py:
from .models import User, draft_new_link_message, load_history, load_messages, db
# Initialize app and such
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keyssss'
socketio = SocketIO(app)
db.init_app(app)
app.app_context().push()
...
db.create_all()
From models.py:
db = SQLAlchemy()
class Link(db.Model):
__tablename__ = 'link'
id = db.Column(db.Integer, primary_key=True, nullable=False)
url = db.Column(db.String(500), nullable=False)
originator_id = db.Column(db.Integer, db.ForeignKey('user.id'))
originator = db.relationship("User", back_populates='history')
From these lines alone, it seems that I should be on one session. If I'm not, how do I format my code correctly to reduce headaches and make sure I don't have to transfer objects between sessions? Thanks!
Edit: Solution
The reason I structured my project this way was because a few pieces of documentation said this was the correct pattern (creating the db inside your models file and then callng db.init_app() to get it into the main file). But I guess this was a bad idea. I thought maybe I had to because I can't have both the files reference each other. But to get around this I wrote a method in the main file to get the db and called the import on the models function
My new __init__.py:
# Initialize app and such
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///browse_together.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keysssss'
socketio = SocketIO(app)
db = SQLAlchemy(app)
# Provide a way for models.py (and any other files that needs it) to get access to the database
def get_db():
return db
# Now you can import models.py because it can use this database
from . import urltils, models
from .models import User, Group, get_groups, create_group, \
draft_new_link_message, load_history, load_messages, toggle_send
The new first few lines from models.py:
from flask_login import UserMixin
from . import urltils
from . import get_db
# Get an instance of the db from __init__
db = get_db()
I think this is more correct.
The reason I structured my project this way was because a few pieces of documentation said this was the correct pattern (creating the db inside your models file and then callng db.init_app() to get it into the main file). But I guess this was a bad idea. I thought maybe I had to because I can't have both the files reference each other. But to get around this I wrote a method in the main file to get the db and called the import on the models function
My new __init__.py:
# Other imports...
# Initialize app and such
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keysssss'
db = SQLAlchemy(app)
# Provide a way for models.py (and any other files that needs it) to get access to the database
def get_db():
return db
# Now you can import models.py because it can use this database
from . import urltils, models
from .models import User, Group, get_groups, create_group, \
draft_new_link_message, load_history, load_messages, toggle_send
The new first few lines from models.py:
from flask_login import UserMixin
from . import urltils
from . import get_db
# Get an instance of the db from __init__
db = get_db()
I think this is more correct.

flask blueprint - database call gives Object has no attribute 'query' error

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.

Flask-SLAlchemy session.add() and session.commit() don't work

I have a heroku app that I am trying to add a database to. I am using Flask-SLAlchemy, PostgreSQL (and psql). I've already created a table in the database, but I cannot add any rows to it. Here is what I believe to be all relevant code:
import flask
import keys
import requests_oauthlib
import json
import os
import psycopg2
import urlparse
from flask import (Flask, jsonify, render_template, redirect, url_for, request, make_response)
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'heroku-url-here'
db = SQLAlchemy(app)
class Page (db.Model):
__tablename__ = "pages"
title = db.Column('Title', db.String)
date = db.Column('Date', db.String, primary_key=True)
writing = db.Column('Writing', db.String)
def __init__(self, title, date, writing):
self.title = title
self.date = date
self.writing = writing
def __repr__(self):
return '<Page %r>' % self.date
app.secret_key = keys.secret_key
# db.create_all()
# this created the database already after I ran it once, it made a psycopg2 error after that first time.
#app.route('/db', methods=['GET', 'POST'])
def db():
if request.method == 'POST':
title = request.form['title']
date = request.form['date']
writing = request.form['writing']
newest = Page(title, date, writing)
print newest
db.session.add(newest)
db.session.commit()
else:
title = None
date = None
writing = None
return flask.redirect(flask.url_for('home'))
In my heroku logs, there are no errors shown. The code runs to the the print newest line, and the newly created Page is printed as <Page u'whatever-the-date-was'>. When a form is submitted in my html template, it calls the function by using the action {{url_for('db')}}.
This is my first time using heroku and flask and basically doing any back-end stuff, so please explain thoroughly if you have an answer. Thanks in advance!
Take advantage of your Page model here.
db.session.add(Page(
title = request.form['title']
date = request.form['date']
writing = request.form['writing']
))
db.session.commit()
You'll probably also run into trouble with your conditional - if the method isn't POST then nothing will happen, and there won't be any message logged about it. If you remove the 'GET' from the methods in the route declaration you won't need that conditional at all.
I'd recommend taking a look at the Flask-WTF extension, as well as breaking out your form validation and redirect steps into separate functions. Flask works best by breaking down elements to their smallest usable components and then reassembling them in many different ways.
For more info on form handling, check out Miguel Grinberg's Flask Mega-Tutorial (if you haven't already).

Categories

Resources