Create your own Telegram bot with Django on Heroku – Part 7 – Introducing apps and URLconf

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

 

Django_Pony

⚠️ This article is outdated and discontinued since Heroku decided to no longer offer their free tiers as this article series suggests to use in August, 2022. Please see this post for details. ⚠️

In the previous part of this series, we started with the basics for kicking off a new Django project. We prepared our virtualenv, installed needed modules to it, created and integrated a new Heroku – project for it and learned how to work with variables in Heroku to control our application with an easy example. We also learned how to check our results locally before we publish it to our production space and how we can add an addon to our Heroku project by adding a PostgreSQL database to it.

Today, we will learn what an “app” is in Django and how to create it. Also, we will learn about and create a so-called URLconf / routing to direct specific URLs to specific parts of our code.

Project, App, Application, … what is all this? ?

In Django, there are a few terms, which are a bit confusing at first. The fact that careless people tend to use them as exchangeable terms sometimes(including myself, like I did in the previous part by writing “Creating the Django app” even though we created a project ?), confuses beginners even more. So: Let’s begin today’s article with a short definition of these terms:

A project is what we have created using the django-admin startproject dtbot . in theprevious part. It is the absolute lowest entry level of the Django structure. For me, it helps the best thinking about what my personal definition of the term “Project” is without thinking about Django, but the term in general. It may be a collection of pieces, which form the stack build for a specific customer, maybe. For each completely new thing, like a different website or a different customer, you are creating an own project for, usually. That’s exactly what it describes in the Django-World, too.
The tutorial describes a project like this:

(A Django project is) a collection of settings for an instance of Django, including database configuration, Django-specific options and application-specific settings.

The terms app and application are harder to distinguish. To make it even more confusing, an “app” is also called a “package“, sometimes. These two terms mean the same thing absolutely and are 100% interchangeable. If any, most people tend to use the term “app” as the thing that lives in your project tree and “package”, when they are talking about a packaged distribution of that code for shipping or download. But generally, these are describing the same thing.
The term “application” does not exist in the Django world as an own term, really. In my experience, people tend to talk about “the application” if “the project” is what they meant in the first place. Another thing which is confusing is the fact that “app” and “application” are not really two different words; “app” is just an abbreviation of the term “application” in the end. Anyways, they are used differently.
An “app” is something which is really doing something active in your project instead of defining settings, routes or basics for anything to base upon, like generating a page, receiving data, applying logic on that requests, and so on.

Again, this is how the Django Tutorial describes the difference between a project and an app:

A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects.

Why multiple apps?

If you are not too familiar with this concept, it might look like creating several apps in one project is making everything more complicated than it is necessary. In the end, you might complain, you are about to create just such a tiny little application which seems like not being worth the efforts to structure everything in such a complicated way.

The last sentence of the previous quote gives an idea for this already: Reusability is a strong reason. Maybe you want to share the result of your development sooner or later. Or, after having finished this one bot, you might have a great idea for another one, dealing with your shopping list in some way instead of your household-budged. Or maybe these two should even interact with each other later? Or the second bot you create simply does not need huge parts of the functionality of your first bot – why should you carry around “dead” code which makes your bot’s code just more complicated and blown up? Wouldn’t it be great if you had created separated apps for things like:

  • User registration
  • Calculations and reporting
  • Analyzing the message and creating the replies

The point is: You can’t know at the beginning how your project evolves over time or what additional ideas you might have. Separating functionality into apps make it appear a bit overcomplicated, but as soon as you have made your first steps with this, it won’t feel like being complicated anymore. Just stick to it and stay tuned until we have created our app in a minute!

If you want to learn more about apps like: “When does it make sense to separate some functionality to separate apps?” I recommend reading this article about it: 0–100 in Django: Starting an app the right way

Creating the app

A new application is created, using a command from the manage.py script in the root of your project-dir:

(dtbot-hT9CNosh) ~/dtbot $ python manage.py startapp bot
(dtbot-hT9CNosh) ~/dtbot $

… that’s not too exciting, is it? To see what this command has changed in our project, I like to use Git to display all differences:

(dtbot-hT9CNosh) ~/dtbot $ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    bot/

nothing added to commit but untracked files present (use "git add" to track)
(dtbot-hT9CNosh) ~/dtbot $ git add .
(dtbot-hT9CNosh) ~/dtbot $ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   bot/__init__.py
    new file:   bot/admin.py
    new file:   bot/apps.py
    new file:   bot/migrations/__init__.py
    new file:   bot/models.py
    new file:   bot/tests.py
    new file:   bot/views.py

(dtbot-hT9CNosh) ~/dtbot $

Seems as if it has created just one additional folder without touching any of our other files. Fine – this way, it isn’t messing things up.

Right now, this is of absolutely no use for us; we need to do some things before we can really start to build something in that app:

Writing a view ?

… a what? What’s that?
A view is … more or less: Code. A function, to be even more precise, which decides what happens to a request that hits your app. The easiest view I can possibly think of is a static reply to any request. In other words: Answering any request with the same, static page.
Before I overcomplicate this with my explanations, let’s head for an example:

Views are created in the file views.py of the app. So, let’s edit the file bot/views.py . After the app was created, the file has some default content:

from django.shortcuts import render

# Create your views here.

Just remove that and replace it with the following:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. This is the bot app.")

Do not think about “How should I know this is what I need to import and use for a simple HTTP response???” too hard for now – just accept it. This is something that comes with the time; in the beginning, you need to read a lot of docs and lookup examples for nearly every baby-step. That’s normal, you are not stupid or so: Everyone needs to get used to this, first!

Apart from that, it’s pretty obvious what happens here, isn’t it? First, a module is imported, which seems to generate a response, which can be served by the web server to the client then.
A function named index  is defined, taking one argument named request . request is not used in that function; so: Not too important what that is. It’s needed in the API of the function anyway though since Django provides this to any view as part of its internal function.

Alright! But – how to reach that now with our browser? We need to define a so-called URLconf for this, which is what follows next.

Creating a URLconf

A URLconf or “routing configuration” is simply a list of URIs, which points towards a view. For example, we could create a URLconf, which calls for all requests to the URL https://our-domain.com/bot/hello  the previously created index view from the bot app.
Even though this is not meaningful really, let’s do that to explain this step-by-step without expecting everything to be self-explanatory:

Open the file dbot/urls.py in your favorite editor. You will notice, that after some comments, there is already one routing in place:

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

Again: Ignore the imports for now. Just take the fact that it works like this for granted for now:
To map the URL /bot/hello to our index – view in bot/views.py, we first need to step back and remember, what we are doing here:

We are writing an app, which eventually can be taken and copied to other Django projects later. This app might have several URLs, pointing to different functions inside of it. Also, a Django project may have several apps installed.
Does it really make sense that the potentially complicated or even conflicting URLconf is handled in one central file of the project?
Would you really like to solve a naming conflict like one app demanding the URL /conf for itself internally when you had decided to use that very same URL to access your app for configuring your project?

Most certainly not. That’s why it’s a common pattern to create sub-paths for each app and “delegating” the URLconf of that branch to the app. This way, you need to create just one single line or each app you are using, instead of dealing with dozens per app and conflicting patterns.
To do that, we are changing the file dbot/urls.py in the following way:

First, we add include to the list of imported elements from django.urls :

from django.urls import path, include

Next, we register the path bot/ to be delegated to that app’s own URLconf by adding the following to the urlpatterns – list:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('bot/', include('bot.urls')),
]

This makes Django search for the file bot/urls.py for additional URLconf configurations for everything below the URI bot/ (like bot/hook or similar).
The file bot/urls.py is not created by executing python manage.py startapp bot ; we need to create that file ourselves.  Let’s do so now with the following content:

from django.urls import path

from . import views

urlpatterns = [
    path('hello/', views.index),
]

And – we are done setting up our demo-URLconf for now!

To test this, run the HTTP server locally and access http://localhost:8000/bot/hello/ with your browser. This should display the text we entered to our index – view before:

Hello world! for bot app

Stop: Hammertime !!!

I have to admit that I underestimated the extent of this article a lot! That’s why I will make a stop here and postpone the rest of the pre-announced content like creating a database, showing additional Heroku – tools, etc. to the next part to not make this a too big and boring thing to read.

Outlook for the next part of the series

We just learned about some terminology, what an app is and how it is created and made available.

In the next article of this series, we will utilize this freshly gained knowledge to create the hook for our bot, finally. Also, we will create the database. And, because of the lesson I just learned: That’s it – nothing more ?

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 6)Jump to next part of this series (Part 8) >>