Using DynamoDB with Flask

Experiment

September 23, 2019
Attention: The following document is an experiment, so take everything as a suggestion or second option. There are probably much better ways to implement this. Read the notes at the end for more. Also, this is not the correct way to model a DynamoDB instance.

In this post I am going to show how to use DynamoDB alongside Flask (with blueprints). Please take into account that this document is not going into detail on how DynamoDB actually works (actually, the databse modeled here is completely wrong; don't use DynamoDB like this), instead, it demonstrates a way you can implement it into your Flask app.

Project structure

We are going to be using a functional blueprint structure. The app should be structured something like this:


        FlaskDynamo/
            app/
                static/
                templates/
                views/
                __init__.py
                config.py
            application.py
        

Remember that you can view and download the whole project going to the GitHub repo.

Dependencies

For this particual tutorial we only need to install Flask and Boto3 (the Amazon Web Services SDK for Python).


        pip install flask
        pip install boto3
        
Suggestion: Use a virtual environment to keep everything clean and in order.

AWS DynamoDB

To make our lives easier, we are going to work with an already existing table on our DynamoDB database. For this example we are going to create a table called FlaskDynamo.

To create a table go to your AWS console > DynamoDB and click on "Create Table". Then, give it the proper name and make two keys: one primary key, called First Name, and a sort key called Last Name.

CreateNewTable
This is merely a silly example, this not the way to model your database

With our table now created, you can manually add a few items if you want. This will help us with the "show all items" example later in this document.

Connection

We are going to create a file called dynamo.py and place it under the app/ folder. This file will serve as the connection to the database.

dynamo.py looks like this:


        # app/dynamo.py
            
        import boto3

        def init_dynamo(table_name="FlaskDynamo"):
            dynamodb = boto3.resource(
                "dynamodb", 
                region_name="us-west-1", 
                aws_access_key_id="my_access_key", 
                aws_secret_access_key="my_secret_key")
            
            table = dynamodb.Table(table_name)
            
            return table
        

Lets break down what is happening.

The first thing we do after importing boto is to define a function called init_dynamo which accepts the parameter table_name. table_name has the name of the table we created manually earlier by default, so you don't need to declare it everytime. If we ever need to use another table that is not our main or default one, we can always call the function with the new argument, for example:


        db = init_dynamo(table_name="other_table")
        

The next part is the actual connection to DynamoDB. We specify the resource that we want to access is in fact DynamoDB, and we provide the region in which the database was deployed along with our access and secret keys.

Warning: It is never a good idea to hard-code your keys. If possible, always access them through environment variables or use AWS Secrets Manager.

The last part is to connect to the table inside our database and return it so we can work with it later.

Views

We are now going to use our init_dynamo function in our views:


        # views/main.py

        from App.dynamo import init_dynamo

        # Get all items on table
        @mn.route("/")
        def index():
            db = init_dynamo()
            all_items = db.scan(
                Select="ALL_ATTRIBUTES")
            return render_template("index.html", all_items=all_items)
        
        # Add new item to table
        @mn.route("/new_item", methods=["GET", "POST"])
        def new_item():
            if request.method == "POST":
                first_name = request.form.get("firstName")
                last_name = request.form.get("lastName")
                
                db = init_dynamo()
                db.put_item(
                    Item={
                        "First Name": first_name,
                        "Last Name": last_name
                })
        
                return redirect(url_for("main.new_item"))
        
            return render_template("new_item.html")
        

We have two routes, one to display all the items (/) and one to add a new item (/new_item). In both routes we call our init_dynamo function, which we use to run action on our table.

In the main route we send all the items on the table to the HTML file. For the 'add new item' route, all we are doing is getting the values we need to create a new item from a form tag.

As stated earlier, we are not going to dive into the actual use of boto3. If you want to know how to use methods like scan or put_item you can check out the official docs.

Rendering in HTML

In the case of the 'show all items' route, once we pass the items with return render_template("index.html", all_items=all_items), we can render them with Jinja.

        
        <div>
            <h1>Flask & DynamoDB</h1>
            <h2>All names</h2>
            {% for item in all_items["Items"] %}
            <div>
            	{{ item["First Name"] }} {{ item["Last Name"] }}
            </div>
            {% endfor %}
        </div>
        

Final notes

Is this the best option to interface Flask with DynamoDB? Probably not, but if you are just messing around with AWS as a beginner then it might be convenient as you would not need to set up any other services. A better way to use DynamoDB with Flask would be to make calls to a custom API using Lambda and API Gateway, since you are already going to be working with AWS anyway.

Thank you for reading and I hope this was helpful. If you notice any mistakes/typos or you have feedback please feel free to send me an email to info@amedpal.com.

Remember that you can find all the source code here.