Welcome to Part 2 of my multi-part tutorial on building a React, Flask, and GraphQL application. Before we get started on building our models, I wanted to first talk about the structure of our application and the file system. As I’ve mentioned, it’s very important for a developer to stay organized – whether it’s in regards to our code, our components, or our file system. Since our project is going to be growing, we need to structure ourselves to allow us to scale accordingly. I’m going to briefly talk about Flask’s application factory and how to structure our file system to help keep our application able to scale.
LARGE APPLICATION STRUCTURE
Let’s say the project I’m working on is called Findr, an application that will help developers find other developers to collaborate with. Here is a basic example of how my projects are usually organized.
For now, we will just be working in the backend section.
APPLICATION FACTORY
In the previous article, we created just a single file application. This is very useful if you just need to do some small task or need a quick backend server. That being said, in the single file application our app instance is created right away and we won’t be able to change any of our configurations. As our application starts to scale and our configurations need to change dynamically, we will want to use a factory function to delay the creation of our application – this is especially useful for testing.
If you haven’t done so already, let’s create a file directory called Findr. Inside of that directory we will create another directory called backend. And finally within that, let’s create a file called __init__.py
. That’s a double underscore, just to be clear! Python will recognize this as the file called when we instantiate the application.
Findr / backend / __init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config # we’ll discuss the config file next
db = SQLAlchemy( )
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
app.config.from_pyfile(“../config.py”)
db.init_app(app)
from .api import api as api_blueprint # We will discuss blueprints shortly as well
app.register_blueprint(api_blueprint, url_prefix=’/api/’)
return app
Here, the create_app( )
function is the application factory. It will take an argument of the name of the configuration type we are using (Development, Production, Testing, etc), which will be stored in a config.py file we will create momentarily. We created an uninitialized extension, SQLAlchemy, outside of the factory, then when we call init_app( )
on it later, it initializes it.
CONFIGURATION FILE
Next, we will create our config.py file. This will hold all of our information for the databases and any future configurations we may need, for example Flask Mail settings to connect your application to GMail, etc.
Findr / config.py
class Config:
SQLALCHEMY_TRACK_MODIFICATIONS = False
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG=True
SQLALCHEMY_DATABASE_URI = 'postgresql://<username>:<password>@localhost:5432/findr'
class TestingConfig(Config):
DEBUG = True
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get(
"TEST_DATABASE_URL")
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'default': DevelopmentConfig}
Let’s break this down. First, we build a class Config that holds data we want all of our subclasses to have. Also we wrote the class method init_app( )
that we saw earlier in our application factory above. Then we have two subclasses: DevelopmentConfig and TestingConfig which demonstrate two different ways to access the SQLAlchemy Database URI – we can write the string out directly, or call it from an environmental variable we may have set up. Finally, there is a dictionary called config
that we also called in the application factory to let us know which configuration we want to use. We have different configurations since, for example, if we are testing our application, we may want to manipulate the database and don’t want it to interfere with our development or production databases.
FLASK BLUEPRINTS
Since we converted to an application factory, the way we initially handled views with @app.route(‘/home’)
won’t do the job anymore. Now that our application is created at runtime, the app.route decorator exists only after create_app( )
is invoked, which is too late.
We can have many different Blueprints in our application, each with their own views, errors, etc. We could write each Blueprint in a single file or create some structure to allow us to scale if we need to. Let’s create our first Blueprint:
Findr / backend / api / __init.py:
from flask import Blueprint
api = Blueprint(‘api’, __name__)
from . import views, errors
First, we imported Blueprint from flask. Then, much like we created our initial application, we created the api blueprint with the name of ‘api’. Finally, we imported the files that are to be associated with this particular blueprint that will be kept in the same directory as this blueprint. These will be files like the view functions and any error handling, etc. It is important that the import comes at the bottom so we can avoid circular dependencies in these files.
We already registered the blueprint with our app back in the application factory where I had commented. So, the last thing we need to talk about in order to get our application up and running, is the application script.
APPLICATION SCRIPT
In the app.py
file in the top-level directory is where the actual application instance is defined. In this file we will need to create the application, declare which configuration we are using, call Flask-Migrate so we can make our migrations to the application for the database, and create the custom context for the flask shell we will use to help debug our code and databases.
from backend import create_app, db
from flask_migrate import Migrate
# We still need to create our models
# The following code you can comment out
# if you want to run the application now
# Otherwise wait until we build these models next
from backend.models import User, Role
app = create_app('development')
migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User = User)
Besides our models, we have a fully functional flask application using an application factory format!
To review, we discussed:
- How to structure a large Flask application
- How to create the Application Factory function
- How to create our config.py file
- How to create a Flask Blueprint to hold our views, errors, etc
- How to create our application script to run all of our code
In the next part of this series, we will see how to build out our models, use SQLAlchemy to store them in the database, connect our Flask backend to GraphQL and write out our schema and resolver functions. See you there!
Top comments (0)