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

7 min read
Spencer P.
Spencer P.
Published February 6, 2020 Updated February 14, 2020

This is the fifth installment of a tutorial series focusing on how to create a full-stack application using Flask and Stream. In this article will go through setting up a customizable user profile page and an introduction to adding content in Stream, we’ll even toss in a brief section on making a testing module to make sure everything is working properly. Be sure to check out the Github repo to help you follow along!

Getting Started

To start off, we will create a user.html template file. Initially, this will return just a user’s username and gravatar and, if you are an admin, the user’s email address. Don’t worry too much about the lack of information just yet, we’re going to be adding in more fields as we go along:

app/templates/user.html
https://gist.github.com/Porter97/d322064e880478fab3fc326663c7a918

Jinja and Data Flow

Meet Jinja: you can see in the file we just created some curly-braces containing a conditional. Jinja gives us a template that allows us to conditionally render a fields flow control, and we are going to be taking advantage of that capability as we go along.

Most of the rest of the file should seem pretty straightforward after that; we are going to pass through a user object with the render_template response, like in our earlier forms, in order to populate the name, gravatar and email fields.

Now that we have our template, let’s set up the route to return it:

app/main/views.py
https://gist.github.com/Porter97/975a06fcea50e5c15369e5396ac05bb3

In the views route, we are taking the username as a variable through the URL path and passing it to the route function. The username is then used as a filter in a query of the User class, which returns a 404 response if the user isn’t found. If the user is found, the function renders the user.html template, passing through the user object to populate the fields of the page.

Getting There

Next, we’ll provide a way for a user to access the page from within the site by adding a profile link to the navbar:

app/templates/base.html
https://gist.github.com/Porter97/5b3a8f1f09175d4464d4573bd7e24c54

Forms on Forms

Beautiful! Now you can view your profile page, and navigate there easily using the navbar. But I would hardly blame anyone for calling the info sparse. For that reason, we should add a little depth to it by allowing users to edit and add more information, should they so choose. For this, we will have to dive back into forms again:

app/main/forms.py
https://gist.github.com/Porter97/a452caf522d47d0e8a99b22f8aeead42

Field Day

As you may have noticed, I added a name field to the “edit profile” form. This means that we will have to update our database model to reflect this new field:

app/models.py
https://gist.github.com/Porter97/8f4e2d958f151583340ac127915288b0

Upgrades People, Upgrades

Since we have updated our User model, we now have a great opportunity to update our database using [alembic](https://flask-migrate.readthedocs.io/en/latest/). The commands are pretty basic:

flask db migrate
flask db upgrade

The migrate command generates a migration script (be sure to always double-check the file to ensure it’s correct), which is then applied with upgrade.

Routed

Now that we have the database up-to-date with the new field, it’s time to set up our routes and template to return the forms. Since the core of the template will stay the same, and only the fields will change, we can create one “edit profile” page for both users and admins, and simply change the form that is provided:

app/main/views.py
https://gist.github.com/Porter97/eb81d9493aaddd4fe45357ec98ec93a6

app/templates/edit_profile.html
https://gist.github.com/Porter97/06984bb1a0c70ed7dc3a9911a7dca314

Last, but not least, we’ll need to create a link on the user page to allow users to edit their profiles, as well as one for admins to do the same. While we do this, we can also show the updated fields using conditionals:

app/templates/user.html
https://gist.github.com/Porter97/58e4144efb90bcfec5109eb68dd1ee0c

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

The result of these changes should give you something like this:

Next Steps

Now that we are finished with our user profile pages (for now), we’ll start to put together a testing module to ensure that the critical functions of our site work properly. In the very top directory of our project, we will create a new directory named “tests”. Within that, create a new __init__.py file, which we will leave blank, as well as a test_basics.py file, and a test_user_model.py file and fill them out like so:

tests/test_basics.py
https://gist.github.com/Porter97/e630b92e8b4951611a6d03be670676cc

tests/test_user_model.py
https://gist.github.com/Porter97/6ee7f5d0033e82137b7195519b4a9183

The tests above should seem pretty self-explanatory, given their function names. Essentially, each one runs through a specific aspect of the app itself or the User model and its functions, testing to make sure that everything is working as it should. The goal when designing tests is to ensure that every function that you use in a site is properly validated and operating the way you designed it to.

Our last task is to create a CLI task that will allow you to run your tests. Opening up our application.py file, use the code that follows:

application.py
https://gist.github.com/Porter97/83dd532e6535ded80aeb310e711fc345

Now, you can run tests through the command line with “flask test”, returning a function-by-function review, along with any errors. Make sure that all the tests return okay and then you’re finished!

Testing The Waters

Over the past five articles we have gone through the creation of the basics in web app development: creating/updating users, project file structure, testing, database migrations, and email, to get us ready to implement more advanced functionality. Next week we will be getting into the real meat of the project, allowing users to create their own collections and managing them effectively. Before we get there though, I would recommend that you play around with the Stream-Python library. As we get further into the project, having a little experience can go a long way in understanding how it all fits together, and how you can create your own projects using this awesome tool!

Bonus Round

I am going to take a few minutes to run through some basic functions outside of the scope of our project to give you an idea of how it works. As always, I recommend that you boot up a venv (python “virtual environment”) to keep your packages isolated from the rest of your development environment.

Opening up a new Python project with an active virtual environment, pip install the stream-python library:

pip install stream-python

Next, either create a new Python file or open a Python terminal, and import the Stream package at the top, instantiating a new client using your API keys found here. Be sure to create a new app in Streamfor this test, as you don’t want to potentially mix data from the test with the rest of our project. We are going to use the “flat feed” type, which I’ve titled ‘user’ for this demo:

test.py
https://gist.github.com/Porter97/593726b4fe7aea208fb0970f96a71d7a

In the feed object, you can see two strings: the first is the feed type that we are using (user), and the second is a user ID. You can use any string you want to represent a user, just make sure that it is unique, you don’t want to have two overlapping IDs. Now that we have created a user feed, we can start adding activities to it with the aptly named add_activity() function. This function takes in the actor (who is doing the activity), the verb (like pin, tweet, or post), the object (the id of the tweet, pin or post in your database), and any other custom fields that you wish to put in there (like a copy of the message itself):

test.py
https://gist.github.com/Porter97/468edbf229d02e44cd79693666a5b72b

Next, we’ll create a second user, and have it follow our first, in order to try out retrieving activities:

test.py
https://gist.github.com/Porter97/bf59978ac01a1fa427e65b58a8faeb53

Running this script, you should see a JSON response with a list of dictionaries, including all of the previous activities of the followed user:

{'results': [{'actor': '1',
              'foreign_id': '', 
              'id': '54aed968-4848-11ea-ad28-0a286b200b2e', 
              'object': '1', 
              'origin': 'user:1',
              'target': '',
              'time': datetime.datetime(2020, 2, 5, 18, 50, 8, 381169),
              'tweet': 'Hello World', 
              'verb': 'tweet'}

One thing to note in the response object is the ID string, using this we will be able to edit a post, delete it, or quickly paginate through activities (which comes in handy for infinite scroll). As such, note that when you upload activities in your own projects, as well as our own later, we are handling the response object when we add an activity and storing it in our own database.

Final Thoughts

Now you have tried out some very basic functionality with Stream, but there is a lot more that we will get into over the course of this project (and a lot that we won’t get to). Once again, I encourage you to play around with the library and check out the documentation to get comfortable using it.

Thanks for reading and happy hacking!

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

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