Create your own Telegram bot with Django on Heroku – Part 6 – Creating the Django app

This entry is part 6 of 7 in the series Create your own Telegram bot with Django on Heroku

 

 

Django_Pony

In the previous part of this series, I tried to give you a brief yet thorough introduction to hosting your projects with Heroku.
That part was special because it was a completely optional part of this series; if you prefer to host your applications on a different platform and skipped that article, I’d like to repeat that this is completely OK and that I had shown nothing you will need for anything different but interacting with Heroku. You will hopefully notice no blank spots in the following articles. There is no need to read that article if you do not plan to use Heroku for hosting your bot. But you should be familiar enough with your hosting solution of choice to adopt the Heroku – commands I show here to an adequate setup for your hosting solution.

Today we will finally start creating our bot with Django. What we did up until now was just some kind of preparation and establishing background. In this part, we will finally start with the real stuff. 💪

Foreword

This article reflects how I created my first Django project. I had some experience with Python before already, but I never created any real Django project apart from the standard tutorial – setups, when one is trying to follow the official Django intro, for example.

When you follow this article, you will end up with a working Telegram bot which I have not found any issue with yet. But neither is it based on any best-practice on how to create Django applications nor will there be any conceptual groundbreaking insights. Maybe you could not even do worse than to follow my instructions when you are unfamiliar with Django and want to learn how it is done; maybe, I do not know better myself. But on the other hand I think I have created something not too bad since it is working as expected, but I doubt that what I’m about to show with this article will be a perfect first attempt.
Because of that, I encourage you to try to get the most out of this article, even if it isn’t perfect and nobody with fundamental Django skills would consider it to be a great start into Django, most certainly. But do not take anything in this article blindly as the one and only truth.

If you are more familiar with Django or do have better ideas with any of the shown codes or concepts, please let me know in the comments. I would really appreciate some feedback; positive or negative. I will adapt valuable advice by updating this article, accordingly.

Providing a proper environment

In this project, we will use the following:

  • Django
    • For our Telegram Bot, we will use Django 2.1.
  • Database
    • To not make the local setup too complicated, we will use a sqlite3 database for local development and testing.
    • For the production application in Heroku, we will use a PostgreSQL database, since this is a great SQL RDBMS and one of the three managed data services, which are available to all customers in Heroku.
      If you want to learn more about PostgreSQL in Heroku please read Herokus article on heroku-postgres.
  • Python
    • Since Heroku’s current default Python is 3.6.6 (as of 2018-08-24), I will also use 3.6; it’s not necessary to exactly match the patch level version.
    • If you want to use Python 3.7 for any reason (utilization of Data Classes for example), you can do so, but you will need an additional step, not explicitly mentioned later on anymore:
      To define Python 3.7 as the desired Python runtime in your project, add  python-3.7.0 to a file called  runtime.txt inside your project folder ( echo 'python-3.7.0' > runtime.txt). Heroku will automatically pick that up and deploy your dynos with this runtime. More on Python runtimes on Heroku in python-runtimes article.

To match this environment, it is recommended to create a new virtualenv for it. I will show how to do this using pipenv in a minute.

Preparing your project

In the previous part of this series (which was optional), we created a new app on Heroku and initialized a simple Flask application in  ~/tbot. We do not need any of that in the upcoming parts since it was meant to demonstrate Heroku’s workflow, exclusively. If you followed that article, you may leave everything in place like it is. If it isn’t used (no one is accessing its URL), it won’t consume any resources.

For this article, we will initiate a new project folder at  ~/dtbot. If you prefer another location, feel free to do so, but please adopt that in the following occurrences of  ~/dtbot  accordingly.

First, we need to have this empty folder created, initialize Git in it and create a fresh virtualenv using pipenv :

Next, we will install some required Python modules:

For those of you who are using Heroku to host your Django bot, install an additional module called django-heroku:

django-heroku automatically pulls in some additional modules:

  • dj-database-url
  • psycopg2
  • whitenoise

With these modules installed, we can simplify and optimize the Django integration with Heroku; please read the project page for details. I will explain how to utilize these shortly.

⚠ Attention:
I’m using pipenv in this guide to populate my virtualenv with modules. This automatically creates a file called  Pipfile.
Heroku’s buildpacks detect and build scripts are checking if a file called  Pipfile or requirements.txt exists in the root of your project to detect the proper buildpack to be Python.
If you do not use pipenv to manage your environment, like plain virtualenv + pip or no virtualenv at all, you need to create a  requirements.txt file manually whenever you change your module composition in your project like this:

(dtbot-hT9CNosh) ~/dtbot $ pip freeze > requirements.txt

This not only allows Heroku’s build process to identify the proper buildpack as Python, but it also tells that build process with which modules it needs to populate the hosting environment with to make your app work. Remember: Every change in your code results not only in the code to be exchanged and some WSGI server to be restarted, but a completely new environment will be created from zero. Heroku needs to know what modules to include in this build for your container; having the  Pipfile or requirements.txt in place is the way you do this.

Create a new Heroku app

While you are inside your local project folder (with the freshly initialized Git repo in it), create a new app in your Heroku environment (for details on this process, please read the previous part of this series):

We will need this, soon. The  --buildpack heroku/python part is optional, yet recommended, since it explicitly sets our buildpack (more on this later) to be Python. Otherwise Heroku would try to figure the proper one out by itself. This also works very well, but since we are Pythonistas, let’s keep it with 🧘 The Zen of Python 🐍:

Explicit is better than implicit.

Activate your virtualenv and initiate your project

We can activate our virtualenv now. I recommend to do it like this:

This last command is a bit special in my personal workflow: I do not like the pipenv default way to spawn a sub-shell using  pipenv shell. So I’m activating the virtualenv pipenv creates by sourcing it’s “activate”-script the classical virtualenv-way.

Now, the time has come to initiate your Django project:

Let’s calibrate some settings in  ~/dtbot/dtbot/settings.py to match Heroku’s recommended implementation first:

  • Add  import django_heroku to the top of your  settings.py file.
    Then, to the very bottom of it, add django_heroku.settings(locals()). By this, some sensitive credentials (like the database credentials) and secrets (like SECRET_KEY) and general settings (like  DATABASE_URL) do not need to be stored in the  settings.py file, but are configured using environment variables inside of the dyno or work out-of-the-box because Heroku provides these values in environment variables automatically.
    This adds some value to your project since you will need to configure less in files. Also, not even your co-workers will get these secrets exposed, your app will become re-usable without the need to make any change to its  settings.py file, et cetera. Even if there are no co-workers, maybe there will be some in the future or you decide to make your bot’s code publically available on Github or similar; you can’t know today what will happen in the future. Because of that, adding sensitive information to a VCS like Git is not recommended generally.
    To learn about the details of this, please read Herokus django-app-configuration article.
  • Add  import os to the top of  settings.py. Then replace  DEBUG = True by this:

    I will explain the logic behind this in a minute.
  • Add  127.0.0.1 to  ALLOWED_HOSTS , so you can access your Django locally.

As a last initialization step, apply any migrations now; these will be applied to the default sqlite3 DB file  db.sqlite3 in your project folder:

That’s it so far – let’s test this and play around with it a bit next.

Start the build-in HTTP server and enable DEBUG

Let’s test drive our setup so far. Run your app now like this:

When you point your browser to  http://127.0.0.1:8000/, you should notice two things:

  1. This should work and a web page should be shown.
  2. The content of the web page should be:
    Not_Found
    If you ever had worked with Django before, you may have expected a DEBUG – page with additional details. This is not shown since we changed  DEBUG = True in the  settings.py file for our environment extraction logic before. By default (when there is no  DEBUG variable with a value of  True assigned to it) this results in  False as a security measure.
    To have the DEBUG – page shown here instead set the environment variable DEBUG to True before starting the HTTP server using  python manage.py runserver like this:

    Now, when you refresh your browser, the DEBUG page should be shown instead:

    django_21_debug_page

Heroku config vars

So – why did we change the way  DEBUG is switched now?
In containerized environments, settings are often defined using environment variables inside the containers, since this enables the users to use reusable applications without changing any files in each container. It’s just more flexible and easier to orchestrate than config file settings, since scaling and multi-node setups are much more a standard pattern in containerization than it is in the bare metal world. Heroku is no exception to this.

You can change and list environment variables for an application in Heroku using the  heroku CLI. To show all registered config vars and their value, just execute  heroku config. New ones can be set or existing ones can be changed using  heroku config:set:

Now, whenever you push a change to your project, it will be deployed with Debugging enabled.
🚨 Most certainly, you do not want that for a productive application 🚨! There are more advanced methods to debug a remote Django web application than to expose it’s debugging data to the world!
But for this tutorial-like application, it is a great and easy example to demonstrate how this concept is working on Heroku.
Leave this set to  True; we will push our Django app to Heroku soon. Then, we will play around with this a bit and finally switch the content of the  DEBUG config var to  False, before we will never touch it again 😉

Prepare our Django app to be shipped 🎁

Before we can deploy our app on Heroku, we need to do two more preparations:

Add a PostgreSQL RDBMS to your Heroku app 🐘

In Heroku, this is only one command:

That’s it!
When you check your app’s config vars, you will notice that a new variable called  DATABASE_URL was added to your application. Also, it now shows up in the list of addons:

This way, your Django app is connected to your PostgreSQL database automatically, since the connection string defined in  DATABASE_URL is picked up by the  django_heroku module we added to our  settings.py file earlier.

This way, you are done preparing a PostgreSQL database for your Heroku hosted web app with just one single command 👍
Isn’t this just awesome?
But there is even more: You can even connect to that DB by extracting the necessary parts for your application (pgAdminIII, psql, …) from that URL. If you want to use psql from your current workstation, there is even a more convenient way:

So, in theory, whenever you need a PostgreSQL DB for a project hosted literally anywhere, you could just create an empty Heroku app, add an heroku-postgres addon to it and start utilizing that DB.

To me, this is pure elegance!

Create a Procfile

Finally, as the last preparation step, let’s get one more detail out of the way: Create a  Procfile to configure how Heroku should startup your dyno with but one line as content:

web: gunicorn dtbot.wsgi

If you want to know more about a  Procfile, please read the previous part of this series or the corresponding Heroku article on the Procfile.

Ship your app! 🛳

We now should have the basics set and be ready to commit our files to the Git repository and to push it to the  heroku remote to trigger a deployment.

🚨 Once more a warning 🚨: Remember that since we still have the config var  DEBUG set to  True, our app will be in debugging mode! We do not really have valuable data in it yet and it is also more than unlikely that in the few minutes between our deployment and the moment when we switch it to  False someone with bad intention will visit our unpublished, randomly generated URL. But please decide this on your own. If you do not feel comfortable with this and do not need to see the debug page on your own environment to get an idea for the environment, better switch config var  DEBUG to  False before you do your push to heroku master.

Deploy your prepared Django app like this:

“remote: … deployed to Heroku” – exactly what we were hoping for to see! When we navigate to our apps URL ( https://dry-tundra-61874.herokuapp.com/ in this example), we should now see the DEBUG page we did before when we tested locally. For convenience, you can simply execute heroku open to direct your browser to the correct URL:

heroku_django_debug_page
… let’s disable the debugging now to make that bad tummy feeling go away before we proceed.
In your shell, simply execute heroku config:set DEBUG=False :

With immediate effect, the debugging mode should be disabled when you refresh your browser:

django_on_heroku_without_debug

Phew, that happened to become quite a long article, hasn’t it?
Because of this, I will stop now and better continue in the next part.

So far, you managed to create your Django project skeleton, prepare it for Heroku and deployed it already! That is enough for one day anyway, I guess.

Outlook for the next part of the series

We just learned how to prepare a proper local environment for Django and Heroku by creating a virtualenv using pipenv. Also, we saw how to kickstart a new Django project, make changes to its config appropriate for Heroku hosting, deploy it to Heroku and how to use Herokus config vars properly to quickly change settings in your running and future deployed apps. Finally, we already had a peek into some of Herokus convenient development features and how to utilize them by the heroku CLI.

In the next article of this series, we will see some more of Herokus features which support you in your development. We also need to do some other actions to finalize our Django app in Heroku; we neither have applied our migrations to the PostgreSQL DB, nor created a superuser to access the Admin Area yet. Finally, we will create a Django app which can receive the JSON data sent by your Telegram bot and do something with it.

If you liked or disliked this article, I’d love to read that in the comments!

Enjoy coding!

Series Navigation<< Go back to previous part of this series (Part 5)Jump to next part of this series (Part 7) >>

Born in 1982, Marc Richter is an IT enthusiastic since 1994. He became addicted when he first put hands on their family’s pc and never stopped investigating and exploring new things since then.
He is married to Jennifer Richter and proud father of two wonderful children, Lotta and Linus.
His current professional focus is DevOps and Python development.

An exhaustive bio can be found at this blog post.

Found my articles useful? Maybe you would like to support my efforts and give me a tip then?