Chapter 2: Hello World app
In this chapter we’ll build a new Django project that says “Hello, World” on the homepage. This is the traditional way to start a new programming language or framework. We’ll also work with git for the first time which is a version control system and deploy our code to GitHub, a remote code repository.
If you become stuck at any point, complete source code for this and all future chapters is available online in the official GitHub repo.
To start navigate to a new directory on your computer. For example, we can create a
helloworld folder on the Desktop with the following commands.
$ cd ~/Desktop $ mkdir helloworld $ cd helloworld
Make sure you’re not already in an existing virtual environment at this point. If you see text in parentheses
() before the dollar sign
$ then you are. To exit it, type
exit and hit
Return. The parentheses should disappear which means that virtual environment is no longer active.
pipenv to create a new virtual environment, install Django and then activate it.
$ pipenv install django==2.2.0 $ pipenv shell
If you are on a Mac you should see parentheses now at the beginning of your command line prompt in the form
(helloworld). If you are on Windows you will not see a visual prompt at this time.
Create a new Django project called
helloworld_project making sure to include the period
. at the end of the command so that it is installed in our current directory.
(helloworld) $ django-admin startproject helloworld_project .
If you use the
tree command you can see what our Django project structure now looks like. (Note: If
tree doesn’t work for you, install it with Homebrew:
brew install tree.)
(helloworld) $ tree . ├── Pipfile ├── Pipfile.lock ├── helloworld_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py 1 directory, 7 files
settings.py file controls our project’s settings,
urls.py tells Django which pages to build in response to a browser or url request, and
wsgi.py, which stands for web server gateway interface, helps Django serve our eventual web pages. The last file
manage.py is used to execute various Django commands such as running the local web server or creating a new app.
Django comes with a built-in web server for local development purposes. We can start it with the
(helloworld) $ python manage.py runserver
If you visit http://127.0.0.1:8000/ you should see our familiar Django welcome page.
Note that the full command line output will contain additional information including a warning about
15 unapplied migrations.
Performing system checks... System check identified no issues (0 silenced). You have 15 unapplied migration(s). Your project may not work properly untilyou apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. April 08, 2019 - 15:58:34 Django version 2.2, using settings 'helloworld_project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Technically this warning doesn’t matter at this point. Django is complaining that we have not yet “migrated” or configured our initial database. Since we won’t actually use a database in this chapter, the warning won’t affect the end result.
However since warnings are still annoying to see, we can remove it by first stopping the local server
Control+c and the running the command
python manage.py migrate. We will cover database migrations at length in future chapters.
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying sessions.0001_initial... OK
What Django has done here is migrate the built-in apps provided for us which, again, we’ll cover properly later in the book.
But now if you execute
python manage.py runserver again you should see the following clean output on the command line:
Performing system checks... System check identified no issues (0 silenced). April 08, 2019 - 16:03:54 Django version 2.2, using settings 'helloworld_project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Create an app
Django uses the concept of projects and apps to keep code clean and readable. A single Django project contains one or more apps within it that all work together to power a web application. This is why the command for a new Django project is
startproject! For example, a real-world Django e-commerce site might have one app for user authentication, another app for payments, and a third app to power item listing details. Each focuses on an isolated piece of functionality.
We need to create our first app which we’ll call
pages. From the command line, quit the server with
Control+c. Then use the
(helloworld) $ python manage.py startapp pages
If you look again inside the directory with the
tree command you’ll see Django has created a
pages directory with the following files:
(helloworld) $ tree ├── pages │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py
Let’s review what each new
pages app file does:
admin.pyis a configuration file for the built-in Django Admin app
apps.pyis a configuration file for the app itself
migrations/keeps track of any changes to our
models.pyfile so our database and
models.pystay in sync
models.pyis where we define our database models, which Django automatically translates into database tables
tests.pyis for our app-specific tests
views.pyis where we handle the request/response logic for our web app
Even though our new app exists within the Django project, Django doesn’t “know” about it until we explicitly add it. In your text editor open the
settings.py file and scroll down to
INSTALLED_APPS where you’ll see six built-in Django apps already there. Add our new
pages app at the bottom:
# helloworld_project/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'pages.apps.PagesConfig', # new ]
We should always add our own local apps at the bottom because Django will read our
INSTALLED_APPS in top down order. Therefore the internal
admin app is loaded first, then
auth, and so on. We want the core Django apps to be available since it’s quite likely our own apps will rely on their functionality.
Another thing to note is you might be wondering why we can’t just list the app name
pages here instead of the much longer
pages.apps.PagesConfig? The answer is that Django creates an
apps.py file with each new application and it’s possible to add additional information there, especially with the Signals framework which is an advanced technique. For our relatively basic application using
pages instead would still work, but we’d miss out on additional options and so as a best practice, always use the full app config name like
We’ll build and add many more apps to our Django projects in later chapters so this pattern will become familiar with time.
Views and URLConfs
In Django, Views determine what content is displayed on a given page while URLConfs determine where that content is going.
When a user requests a specific page, like the homepage, the URLConf uses a regular expression to map that request to the appropriate view function which then returns the correct data.
In other words, our view will output the text “Hello, World” while our url will ensure that when the user visits the homepage they are redirected to the correct view.
This interaction is frequently very confusing to newcomers so let’s map out the order of a given HTTP request/response cycle. When you type in a URL, such as
https://djangoforbeginners.com the first thing that happens within our Django project is a URLpattern is found that matches the homepage. The URLpattern specifies a view which then determines the content for the page (usually from the database) and a template for styling. The end result is sent back to the user as an HTTP response.
URL -> View -> Model (typically) -> Template
Let’s start by updating the
views.py file in our
pages app to look as follows:
# pages/views.py from django.http import HttpResponse def homePageView(request): return HttpResponse('Hello, World!')
Basically we’re saying whenever the view function
homePageView is called, return the text “Hello, World!” More specifically, we’ve imported the built-in
HttpResponse method so we can return a response object to the user. Our function
homePageView accepts the
request object and returns a response with the string
Now we need to configure our urls. Within the
pages app, create a new
(helloworld) $ touch pages/urls.py
Then update it with the following code:
# pages/urls.py from django.urls import path from .views import homePageView urlpatterns = [ path('', homePageView, name='home') ]
On the top line we import
path from Django to power our
URLpattern and on the next line we import our views. By using the period
.views we reference the current directory, which is our
pages app containing both
urls.py. Our URLpattern has three parts:
- a Python regular expression for the empty string
- specify the view which is called
- add an optional url name of
In other words, if the user requests the homepage, represented by the empty string
'' then use the view called
We’re almost done. The last step is to configure our project-level
urls.py file too. Remember that it’s common to have multiple apps within a single Django project, so they each need their own route.
Update the existing
helloworld_project/urls.py file as follows:
# helloworld_project/urls.py from django.contrib import admin from django.urls import path, include # new urlpatterns = [ path('admin/', admin.site.urls), path('', include('pages.urls')), # new ]
include on the second line next to
path and then created a new urlpattern for our
pages app. Now whenever a user visits the homepage at
/ they will first be routed to the
pages app and then to the
It’s often confusing to beginners that we don’t need to import the
pages app here, yet we refer to it in our urlpattern as
pages.urls. The reason we do it this way is that the method
django.urls.include() expects us to pass in a module, or app, as the first argument. So without using
include we would need to import our
pages app, but since we do use
include we don’t have to at the project level!
We have all the code we need now! To confirm everything works as expected, restart our Django server:
(helloworld) $ python manage.py runserver
If you refresh the browser for http://127.0.0.1:8000/ it now displays the text “Hello, world!”
In the previous chapter we also installed git which is a version control system. Let’s use it here. The first step is to initialize (or add) git to our repository.
(helloworld) $ git init
If you then type
git status you’ll see a list of changes since the last git commit. Since this is our first commit, this list is all of our changes so far.
(helloworld) $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) Pipfile Pipfile.lock db.sqlite3 helloworld_project/ manage.py pages/ nothing added to commit but untracked files present (use "git add" to track)
We next want to add all changes by using the command
add -A and then
commit the changes along with a message describing what has changed.
(helloworld) $ git add -A (helloworld) $ git commit -m 'initial commit'
Please note Windows users may receive an error git commit error: pathspec ‘commit’ did not match any file(s) known to git which appears to be related to using single quotes
'' as opposed to double quotes
"". If you see this error, using double quotes for all commit messages going forward.
It’s a good habit to create a remote repository of your code for each project. This way you have a backup in case anything happens to your computer and more importantly, it allows for collaboration with other software developers. The two most popular choices are GitHub and Bitbucket which both now offer private repositories for individual developers.
When you’re learning web development, it’s best to stick to private rather than public repositories so you don’t inadvertently post critical information such as passwords online.
To get started on GitHub, sign up for a free account on the homepage. Next you’ll be asked to verify your account by solving a simple puzzle meant to discourage automatic signups by bots.
Then confirm you want a Free subscription which is the default selection. Click the “Continue” button at the bottom of the page.
The third step asks several questions to tailor your experience on Github. Check the boxes that apply or scroll to the bottom of the page and select “Skip this step” to continue.
The final step will be a page asking that you verify your email address. Go into your inbox, find the email from Github, and click the appropriate link which will take you back to the Github website now logged in.
Now create our first repository by navigating to https://github.com/new.
Enter the repository name
hello-world and click on the radio button next to “Private” rather than “Public”. Then click on the button at the bottom for “Create Repository”.
Your first repository is now created! However there is no code in it yet. Scroll down on the page to where it says “…or push an existing repository from the command line”. That’s what we want.
Copy the text immediately under this headline and paste it into your command line. Note that my username is
stillriverpress here; yours will be different so if you copy my snippet below it won’t work! This syncs the local directory on our computer with the repository on the GitHub website.
(helloworld) $ git remote add origin https://github.com/stillriverpress/hello-world.git
The last step is to “push” our code to GitHub.
(helloworld) $ git push -u origin master
Hopefully this command works and you can go back to your GitHub page and refresh it to see your local code now hosted online.
Unfortunately there is a good chance the last command yielded an error if you are a new developer and do not have SSH keys already configured.
ERROR: Repository not found. fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
This cryptic message means we need to configure SSH keys. This is a one-time thing but a bit of a hassle to be honest.
SSH is protocol to ensure private connections with a remote server. Think of it as an additional layer of privacy on top of username/password. The process involves generating unique SSH keys and storing them on your computer so GitHub can access them.
First check whether you have existing SSH keys. Github has a guide to this that works for Mac, Windows, and Linux. If you don’t have existing public and private keys, you’ll need to generate them. GitHub again has a guide on doing this.
Once complete you should be able to execute the
git push -u origin master command successfully!
It’s normal to feel overwhelmed and frustrated if you become stuck with SSH keys. GitHub has a lot of resources to walk you through it but the reality is its very intimidating the first time. If you’re truly stuck, continue with the book and come back to SSH Keys and GitHub with a full nights sleep. I can’t count the number of times a clear head has helped me process a difficult programming issue.
Assuming success with GitHub, go ahead and exit our virtual environment with the
(helloworld) $ exit
You should now see no parentheses on your command line, indicating the virtual environment is no longer active.
Congratulations! We’ve covered a lot of fundamental concepts in this chapter. We built our first Django application and learned about Django’s project/app structure. We started to learn about views, urls, and the internal web server. And we worked with git to track our changes and pushed our code into a private repo on GitHub.
Continue on to Chapter 3: Pages app where we’ll build and deploy a more complex Django application using templates and class-based views.