Series: Building a Social Network with Flask & Stream – Part 2

Spencer P.
Spencer P.
Published January 23, 2020 Updated February 5, 2020

This is the second installment of a tutorial series focusing on how to create a full-stack application using Flask and Stream. This week we’re going to start digging into the actual construction of our app. In this article, I am going to guide you through setting up the initial files and structure, as well as registering for Stream and getting you familiar with some of its core concepts to get more familiar with it. Check out the Github repo here to help you follow along!

Getting Started

The first thing we have to do is create a new directory to house our project in. I am going to name mine "offbrand-tutorial", but feel free to name yours whatever you’d like. After that, we are going to boot up a virtual environment(venv). Opening your command-line tool, navigate to the project directory you just created. If you haven’t already, install venv with pip.

pip install python3-venv

Next, we are going to initialize the virtual environment in the project:

python3 -m venv venv

This will create a new virtual environment named venv within your project, which helps to keep all of your Python packages together and separated from your local computer to avoid conflicts between libraries. Before we continue, make sure you activate your environment.

(WIth Windows)

venv\Scripts\activate

Back to Basics

When dealing with a project of this scale, the organization is critical. With that in mind, I am going to be using the app factory design and trying my best to keep elements modular to make it easier to add and remove functionality. I am going to start by creating a basic structure that will include features that we will deal with right away, but also a few that will come in handy over the next few articles and tend to be used over and over in your Flask projects.

Pip Install Everything

I typically like to install a handful of Python packages when I start a new project that I know I’ll be using during development. I find this helps in allowing me to write instead of switching back and forth from the terminal to fetch a new library. So, in your command line console:

Pip install stream-python flask-bootstrap flask-sqlalchemy flask-moment

Stream-Python is going to be our workhorse; it will be acting as our data store for the majority of this project, communicating with the Stream API.

Next is Flask-Bootstrap, which brings in Bootstrap to our templates. A word to the wise on this particular library: Bootstrap doesn’t always play well with every Javascript module out there. If you are going to be bringing in third-party JS libraries, you may have to edit the package or leave it out altogether, an understanding that I came to after 16 hours of very frustrating debugging.

After that, flask-sqlalchemy will be our tool to talk to our sqlite database, and flask-moment will help convert user-facing timestamps in our application.

Testing the Waters (Pun Fully Intended)

As I mentioned earlier, Stream is going to make up the core of our application. Signing up is a quick process, which you can do either with email or your Github account. We will be creating our feeds as the series progresses and can integrate them one by one into our app. I wanted to take a little bit of time, however, to get you more acquainted with how feeds in Stream work, and how we’ll be integrating them into our app. The first thing to know is that there are three different types of feeds available.

Flat Feeds

Flat feeds are typically your bread and butter. They are activity-based feeds that can be combined with other feeds, through follows, to give you a “timeline”-like view. They work by adding “activities” for an object (like a user or a group/place) to the feed – through which all of the followers of that feed will be able to see in their own. We are going to be using this type extensively for organizing user/collection content.

Aggregated Feeds

Aggregated feeds work in a similar way to flat feeds, but with some critical differences. The first is that they aggregate (makes sense) activity in the feed, like “Johnny and two others watched this video”. The other important part is that Aggregated Feeds cannot be followed. Aggregated feeds provide a rather vital service, though, in that if one of your friends gets bored one day and decides to post 100 times in a row, your feed won’t get hijacked. We will be using this type of feed for our user home page.

Notifications

Unless you’ve lived under a rock for the last ten years, the concept of notifications shouldn’t be entirely new to you (which if you have and still learned modern programming, respect). This particular feed group has some rather handy features, though. First, read counts. Typically, this functionality is pretty write heavy on your database when you implement it yourself, which, once you start hitting any scale is a non-starter if you are trying to keep expenses low. Also, it will give you a convenient count of those unseen/unread notifications.

Second, real-time. You can set up your applications to receive notifications in real-time using Webhooks or AWS SQS, which helps to make your applications more dynamic and user-friendly. The alternative would be to make your users refresh the page every time they want to see new notifications. Notifications also automatically aggregate events like an aggregated feed. We will be using Notifications in the second half of our tutorial when we get into React.

Next Steps

Now that we’ve covered the basics, we’ll launch into creating the main directories and files for our app. When we are finished, you will have an application structure that looks like this:

App-etizer

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!

In the app directory, we’re going to create a few basic files and (yet another) directory. The first file we will be creating is an extensions.py file. This is going to gather together the third party libraries that rely on the app context to function.

app/extensions.py

from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_moment import Moment

bootstrap = Bootstrap()
db = SQLAlchemy()
moment = Moment()

After that, we will create an init.py file to initialize (get it?) the app using Flask and register the different blueprints. Don’t stress about the config file or the original plan yet; we will get to those soon.

app/init.py

from flask import Flask
from .extensions import bootstrap, db, moment
from config import config

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    db.init_app(app)
    moment.init_app(app)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app

Finally to round out the /app directory, create two empty files: decorators.py and models.py, as well as a quick exceptions.py file with a ValidationError function:

app/exceptions.py

class ValidationError(ValueError):
	pass

We will talk more about the models and decorators next week, but these are used in almost every Flask application, so like the packages we installed earlier, I’ve found it’s better to get in the habit of creating them with every new project.

Main Course

Next on our docket is to create /main directory for actually returning something when a user visits your page. Create an init.py file in /main, followed by views.py. In init.py, we’ll make our blueprint.

app/main/init.py

from flask import Blueprint

main = Blueprint(‘main’, __name__)

from . import views

And we will create a basic “hello world” endpoint as a sanity check for when we’re done.

app/main/views.py

from . import main

@main.route(‘/’)
def index():
  return “Hello World!”, 200

Finishing up

In our last few steps, we are going to create an application.py and config.py file in our main project directory. This will run the create_app function that we made in the app/init.py file, to operate as the entry point for the web app.

application.py

import os
from app import create_app, db

app = create_app(os.getenv(‘FLASK_CONFIG’) or ‘default’)

The config.py file will organize all of the different project-wide settings as well as help differentiate between development, testing, and production settings for our database.

config.py

import os

basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
	SECRET_KEY = os.environ.get(‘SECRET_KEY’, ‘secret’)
	STREAM_API_KEY = ‘’#Insert your Stream API key her
	STREAM_SECRET = ‘’#Insert your Stream Secret here

	@staticmethod
	def init_app(app):
		pass

class DevelopmentConfig(Config):
	DEBUG = True
	SQLALCHEMY_DATABASE_URI = os.environ.get(‘DEV_DATABASE_URL’) or \
		‘sqlite:///’ + os.path.join(basedir, ‘data-dev.sqlite’)

class TestingConfig(Config):
	TESTING = True
	SQLALCHEMY_DATABASE_URI = os.environ.get(‘TEST_DATABASE_URL’) or \
		‘sqlite://’
	WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
	SQLALCHEMY_DATABASE_URI = os.environ.get(‘DATABASE_URL’) or \
		‘sqlite:///’ + os.path.join(basedir, ‘data/sqlite’)

	@classmethod
	def init_app(cls, app):
		Config.init_app(app)

config = {
	‘development’: DevelopmentConfig,
	‘testing’: TestingConfig,
	‘production’: ProductionConfig,
	
	‘default’: DevelopmentConfig
} 

Final Thoughts

You now should have the basic structure in place to build a Flask Application. To run the sanity check I mentioned earlier to make sure everything is working the way it should, go to the venv activated command line:

(Windows)
https://gist.github.com/Porter97/5374a9d557df56bee4f48377327e6edb

Now, if you navigate in your web browser to localhost (http://127.0.0.1:5000), you should see Hello World!

See you next week, where we’ll be starting to building out our web app with users and permissions levels, as well as some necessary forms for registration and login/logout.

Happy Coding!

Note: The next post in this series can be found here.

Integrating Video With Your App?
We've built a Video and Audio solution just for you. Check out our APIs and SDKs.
Learn more ->