Part 2: Using A Bootstrap Template To Parse STEEM Posts Via Beem API

banner.png

<p dir="auto">This tutorial is part of a series where different aspects of quickly creating and deploying STEEM web applications by using the Django framework as well as Beem are discussed. Knowledge of programming with Python is advised, as well as HTML and CSS. <hr /> <h4>Repository <p dir="auto"><br /><span><a href="https://github.com/holgern/beem" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">https://github.com/holgern/beem<span> <a href="https://github.com/django/django" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">https://github.com/django/django <h4>What will I learn <ul> <li>Importing a bootstrap theme <li>Extracting urls from text <li>Parsing STEEM posts into a template <h4>Requirements <ul> <li>Python 3.7 <li>Django 2.1.5 <li>Beem 0.20.17 <li>Gunicorn 19.9.0 <li>Git <li>Heroku <li>Pipenv <h4>Difficulty <ul> <li>basic <hr /> <h3>Tutorial <h4>Preface <p dir="auto">Django allows for quick development of web applications and since it uses Python as it's main programming language it also allows for easy combition with the steem-python library to develop STEEM web applications. This part builds on the previous part <a href="https://steemit.com/@steempytutorials/part-0-create-steem-web-applications-with-django-and-steem-python" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Part 0 and continues with the code: <a href="https://github.com/Juless89/django-steem-account" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Github. A bootstrap template will be imported and changed to display STEEM posts. <h4>Setup <p dir="auto">Create a new folder for the project and set up a virtual environment with all the required packages. <pre><code>$ cd ~/ $ mkdir steem_blog $ cd steem_blog $ pipenv install django==2.1.5 $ pipenv shell (steem_blog) $ pip install beem==0.20.17 (steem_blog) $ pip install gunicorn==19.9.0 <h4>Importing a bootstrap theme <p dir="auto">Bootstrap is like lego for html and offers many prebuild sets of html/css code. Including themes where multiple sets are combined for some generic use cases. In this tutorial the example theme Album will be used to parse blog posts from a STEEM account that is retrieved from the url. <p dir="auto">To be able to run bootstrap code a start template is required to meet all required .css and <code>.js requirements. In addition each theme may come with some addition <code>.css and <code>.js which has to be installed manually. All the files can be found inside the bootstrap <a href="https://getbootstrap.com/" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">source code. These files are called static files and are held in their separate folder static, which has to be registered in <code>settings.py. Create the required folders <pre><code>(steem_blog) $ mkdir static (steem_blog) $ cd static (steem_blog) $ mkdir css (steem_blog) $ mkdir img #images are also held here <pre><code># steem_account/settings.py STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static') ] <p dir="auto">As every page in the web app will require the starter template it can be kept inside it's own file <code>base.html that is then extended to from other html files. In this case <code>album.html extends to <code>base.html. <pre><code>(steem_blog) $ cd ../ (steem_blog) $ touch templates/base.html (steem_blog) $ touch templates/album.html <p dir="auto">Remove the old home.html and set the view in <code>posts/view.py to <code>album.html. <pre><code>(steem_blog) $ rm templates/home.html # posts/view.py class HomePageView(ListView): template_name = 'album.html' <p dir="auto">Inside <code>base.html several things are happening. <code>load static loads the path to the <code>static folder for any calls to those files. <code>spaceless removes all whitespace between tags >< from the html file for faster loading and <code>block content refers to a block of html that is pulled from a different html file where this block is defined. <pre><code># templates/base.html {% load static %} {% spaceless %} {% endspaceless %} {% block content %} {% endblock content %} <p dir="auto">Copy <code>album.css from the bootstrap source code into <code>static/css and add it into <code>base.html. And <code>_includes/icons/placeholder.svg into <code>static/img. <pre><code># templates/base.html (html comment removed: Bootstrap CSS ) <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> <link rel="stylesheet" href="{% static 'css/album.css' %}"> # new <p dir="auto">Inside <code>album.html that is taken from the bootstrap source code four changes are made. The file is set to extend to <code>base.html. The <code>block content is defined, this is the part that will be parsed into <code>base.html. A for loop is created to generate the html for each blog post and code that is not required from the bootstrap theme is removed. <pre><code># templates/album.html {% extends 'base.html' %} {% block content %} {% endblock content %} {% for post in all_posts_list %} {% endfor %} <p dir="auto">Remove the following div code block. <pre><code><section class="jumbotron text-center"> ... </section> <p dir="auto">Also remove the repeated card html block, put only one between <code>{% for post in all_posts_list %} -{% endfor %}. <p dir="auto">Running the server at this point generates the following. As shown layout is there, however there are some important things missing. <p dir="auto"><center><br /> <img src="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmY22b8H4Sjtug71fLRQJHovZtcViNoqiwKrsLGrqAt3FM/Screenshot%202019-01-31%2022.28.47.png" alt="Screenshot 2019-01-31 22.28.47.png" srcset="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmY22b8H4Sjtug71fLRQJHovZtcViNoqiwKrsLGrqAt3FM/Screenshot%202019-01-31%2022.28.47.png 1x, https://images.hive.blog/1536x0/https://cdn.steemitimages.com/DQmY22b8H4Sjtug71fLRQJHovZtcViNoqiwKrsLGrqAt3FM/Screenshot%202019-01-31%2022.28.47.png 2x" /> <h4>Extracting urls from text <p dir="auto">The previous tutorial showed how a STEEM username can be extracted from the url to retrieve their blog post history via <code>Beem. This returned a generator object which contained a <code>Comment() object for each blog post. The <a href="https://github.com/holgern/beem/blob/master/beem/comment.py" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Comment() class from <code>Beem contains many properties and functioned that can be called upon. One of those is the <code>body, which is the text part of the post. The body post also contains the urls to any images contained inside the post. <code>findall() allows to search for strings that match a regular expression like http urls, this example regular expression works quite well but not on 100% of the urls. <pre><code># retrieve account blog history def account_history(username): stm = Steem("https://steemd.minnowsupportproject.org") account = Account(username, steem_instance=stm) data = account.blog_history(limit=9, reblogs=False) return_data = [] # try to find the first url for the thumbnail image for blog in data: url = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[./\-%]|[!*,]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', blog.body) return_data.append([blog, ('https://steemitimages.com/640x480/' + url[0])]) return return_data <p dir="auto">The image for the thumbnail is always located at the first index. steemitimages.com has a feature to produce thumbnail images by adding a url in front of the image url. Thumbnail image locations are not stored inside the post. The thumbnail and blog object are stored together into a list of lists to be parsed into the html template. <h4>Parsing STEEM posts into a template <p dir="auto">Change the first div class to <code>col-sm-4 d-flex pb-3 so that all cards remain equal in height. Now replace the <code>svg placeholder with the thumbnail image and set the standard <code>heigh and <code>width. To access the second index of the list, instead of <code>list[1] use <code>list.1. Also extract the <code>title, <code>authorperm and <code>reward from the comment object and set them in the appropriate places. <pre><code>{% for post in all_posts_list %} <div class="col-sm-4 d-flex pb-3"> # changed <div class="card mb-4 shadow-sm"> <img src="{{ post.1 }}" class="card-img-top" alt="" height="220" width="512"> # changed <div class="card-body h-100"> <p class="card-text"><b>{{ post.0.title }}</b></p> # changed <div class="d-flex justify-content-between align-items-center"> <div class="btn-group"> <a href="https://www.steemit.com/{{ post.0.authorperm }}"> # changed <button type="button" class="btn btn-sm btn-outline-secondary">View</button> </a> </div> <small class="text-muted">${{ post.0.reward }}</small> # changed </div> </div> </div> </div> {% endfor %} <h4>End result <p dir="auto">Desktop <p dir="auto"><center><img src="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmXVGoWPvnxhhWEEwSbV3rMmzn4mFCVp149fHjSkbxWtbx/Screenshot%202019-02-01%2000.16.56.png" alt="Screenshot 2019-02-01 00.16.56.png" srcset="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmXVGoWPvnxhhWEEwSbV3rMmzn4mFCVp149fHjSkbxWtbx/Screenshot%202019-02-01%2000.16.56.png 1x, https://images.hive.blog/1536x0/https://cdn.steemitimages.com/DQmXVGoWPvnxhhWEEwSbV3rMmzn4mFCVp149fHjSkbxWtbx/Screenshot%202019-02-01%2000.16.56.png 2x" /> <p dir="auto">Mobile <p dir="auto"><center><img src="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmcuHTcD4sHJ3HuxhWVHbZy9feUjgKare5QTaaoJpUbkcY/Screenshot%202019-02-01%2000.37.38.png" alt="Screenshot 2019-02-01 00.37.38.png" srcset="https://images.hive.blog/768x0/https://cdn.steemitimages.com/DQmcuHTcD4sHJ3HuxhWVHbZy9feUjgKare5QTaaoJpUbkcY/Screenshot%202019-02-01%2000.37.38.png 1x, https://images.hive.blog/1536x0/https://cdn.steemitimages.com/DQmcuHTcD4sHJ3HuxhWVHbZy9feUjgKare5QTaaoJpUbkcY/Screenshot%202019-02-01%2000.37.38.png 2x" /> <p dir="auto">Due to Bootstrap the website is responsible and automatically adjusts for desktop and mobile users. Also the reward in $ for pending posts does not appear to be working yet. <h4>Version control and deployment <p dir="auto">Make sure that the pipfile has the following packages and lock the file. <pre><code>[packages] django = "==2.1.5" beem = "==0.20.17" gunicorn = "==19.9.0" pyyaml = "==4.2b4" <pre><code>(steem_blog) $ pipenv lock <p dir="auto">Heroku requires a Procfile, create it and add the following line. <pre><code>(steem_blog) $ touch Procfile # ./Procfile web: gunicorn steem_blog.wsgi --log-file - <p dir="auto">Now set the <code>ALLOWED_HOSTS in <code>settings.py so the website can run from any ip. <pre><code># steem_blog/settings.py ALLOWED_HOSTS = ['*'] <p dir="auto">Create a git repository online. Now initiate git in the folder, add all the files, commit and push. <pre><code>(steem_blog) $ git init (steem_blog) $ git add -A (steem_blog) $ git commit -m 'initial commit' (steem_blog) $ git remote add origin https://github.com/Juless89/django-steem-blog.git (steem_blog) $ git push -u origin master <p dir="auto">When using git for the first time it is recommend to set the following variables. <pre><code>$ git config --global user.name "Your Name" $ git config --global user.email "yourname@email.com" <p dir="auto">Create an Heroku app and link it. <pre><code>(steem_blog) $ heroku login (steem_blog) $ heroku create Creating app... done, ⬢ enigmatic-meadow-62592 https://enigmatic-meadow-62592.herokuapp.com/ | https://git.heroku.com/enigmatic-meadow-62592.git (steem_blog) $ heroku git:remote -a enigmatic-meadow-62592 set git remote heroku to https://git.heroku.com/enigmatic-meadow-62592 <p dir="auto">Push the code to heroku. <pre><code>(steem_blog) $ git push heroku master <p dir="auto">Activate the Henroku app, set it to level 1. Level 1 is free to use. Then open the app to see it live. As this is a free service the apps get put to sleep when not used for 30 min. <pre><code>(steem_blog) $ heroku ps:scale web=1 (steem_blog) $ heroku open <p dir="auto"><span><a href="https://enigmatic-meadow-62592.herokuapp.com/@steempytutorials" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">https://enigmatic-meadow-62592.herokuapp.com/@steempytutorials <h3>Curriculum <ul> <li><a href="https://steemit.com/@steempytutorials/part-0-create-steem-web-applications-with-django-and-steem-python" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Part 0: Create STEEM web applications with Django and Steem-Python <li><a href="https://steemit.com/utopian-io/@steempytutorials/part-1-using-the-url-to-dynamically-pull-data-via-the-steem-api-and-parse-to-html" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Part 1: Using the URL to dynamically pull data via the Steem API and parse to html <hr /> <p dir="auto">The code for this tutorial can be found on <a href="https://github.com/Juless89/django-steem-blog" target="_blank" rel="noreferrer noopener" title="This link will take you away from hive.blog" class="external_link">Github! <p dir="auto"><span>This tutorial was written by <a href="/@juliank">@juliank.
Sort:  


After analyzing your tutorial we suggest the following:Thank you for your contribution @steempytutorials.

  • The images of the mobile version are very large, in the next tutorial try to put the smaller images a bit.

Your tutorial is very well structured and well explained. Good work on developing this tutorial.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Thank you for your review, @portugalcoin! Keep up the good work!

I'm not a big fan of Django, because years ago it was a pretty big pain in the ass, and quite bloated. Not sure if it's a bit easier to use now days.

But good job puttin out a tutorial.

Thanks, I am not too familiar with Django yet myself. Doing these tutorials allows me to learn more. I would also like to learn more about Flask after I have played around for a bit in Django.

I like Flask. It's very lightweight and easy to use.

Hey, @steempytutorials!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
SteemPlus or Steeditor). Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!