Set up content, understand Django urls, templates and apps

Problem

You want to see your content on a Django project but don't know how to access it or where to place it.

Solution

Content on Django projects works with three major building blocks: urls, templates and apps. You create and configure Django urls, templates and apps separately, though you connect one to another to fulfill content delivery. Urls define the entry points or where to access content. Templates define the end points that give form to the final content. And apps serve as the middleware between urls and templates, altering or adding content from a database or user interactions.

To run static content you only need to create and configure Django urls and templates. To run dynamic content -- built from a database or user interactions -- you also need to create and configure Django apps, in addition to urls and templates.

How it works

Before describing how to create and configure urls, templates and apps, it's important to understand how each of these parts interact. Figure 1 shows a Django workflow for user requests and how they work with Django urls, templates and apps.

Figure 1 - Django workflow for urls, templates and apps
Django workflow for urls, templates and apps

Create and configure Django urls

The main entry point for Django urls is the urls.py file created when you start a project -- if you're unfamiliar with a Django project structure, see the 'Start a Django project' recipe, 'Django project structure' listing . If you open the urls.py file, you'll notice the urls.py file only has one active url to /admin/ which is the Django admin -- I will discuss the Django admin in the next recipe. Now that you're familiar with the urls.py syntax, let's activate a url to view custom content on the home page of a Django project.

Django urls use regular expressions to match incoming requests. The regular expression pattern to match a home page is ^$ -- the appendix Regular expressions for Django Urls includes more regular expression examples. In addition to the regular expression pattern, an action of what to do when requests are intercepted for matching pattern is also needed (e.g. send the content from a specific template).

Open the urls.py file and add line 3 -- the one below from django.contrib import admin -- and line 9 -- the one below url(r'^admin/', admin.site.urls), -- as illustrated in listing 1.

Listing 1 - Django url for home page to template

from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
...
...

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$',TemplateView.as_view(template_name='homepage.html')),
]

As illustrated in listing 1, urlpatterns is a Python list of url() statements. The url method comes from the django.conf.urls package. The url method you just added defines the pattern for the home page -- the regular expression ^$ -- followed by an action TemplateView.as_view(template_name='homepage.html'). This last action is a helper method to direct the action to a template which takes the argument template_name='homepage.html'.

In summary, the url method you added in listing 1 tells Django that requests for the home page should return the content in the template homepage.html.The url method is very versatile and can accept several variations, I'll describe some of these later.

Now test the home page. Start the development web server by executing python manage.py runserver on the Django project's BASE_DIR. Open a browser on the default address http://127.0.0.1:8000/. What do you see ? An error page with Exception Type: TemplateDoesNotExist homepage.html. This error is caused because Django can't locate the homepage.html template defined for the url. In the next section, I'll show you how to configure and create templates.

Note OperationalError - no such table: django_session

If you receive the error OperationalError - no such table: django_session instead of the TemplateDoesNotExist homepage.html error, this means the database for a Django project is still not setup properly. You'll need to run python manage.py migrate in a project's BASE_DIR so Django creates the necessary tables to keep track of sessions.

See the previous recipe Set up a database for a Django project for more details.

Create and configure Django templates

By default, Django templates are interpreted as HTML. This means Django templates are expected to have a standard HTML document structure and HTML tags (e.g. <html>, <body>). You can use a regular text editor to create Django templates and save the files with an .html extension.

Lets create a template for the url in the past section. In a text editor, create a file named homepage.html and place the contents of listing 2 into it. Save the file on your system, preferably under a Django's PROJECT_DIR in a sub-directory with a name like templates.

Listing 2 - Template homepage.html

<html>
 <body>
  <h4>Home page for Django</h4>
 </body>
</html>

Once you have a directory with Django templates, you need to configure Django so it can find the templates in this directory. In the settings.py file of the Django project, you need to define the template directory in the DIRS property of the TEMPLATES variable. The DIRS property is a list, so you can define several directories to locate templates, though I recommend you only use a single directory with various sub-directories for classification.

As I mentioned previously, you should aim to keep Django templates inside a sub-directory of a Django's PROJECT_DIR. So for example, if a Django absolute path to PROJECT_DIR is /www/STORE/coffeehouse/, the recommended location for a DIRS value would be /www/STORE/coffeehouse/templates/. Listing 3 illustrates a sample DIRS definition in settings.py using the PROJECT_DIR reference variable set dynamically at the top of settings.py.

Listing 3 - TEMPLATES and DIRS definition in settings.py

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['%s/templates/' % (PROJECT_DIR),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Start the Django development web server once again and open a browser on the default address http://127.0.0.1:8000/. Instead of the error page you saw in the previous section, you should now see the content's of the template homepage.html on the home page.

Create and configure Django apps

Django apps are used to group application functionality. If you want to work with content from a database or user interactions you have to create and configure Django apps. A project can contain as many apps as you need. For example, if you have a project for a coffee house, you can create an app for stores, another app for drinks, another app for about information and create additional apps as they're needed. There's no hard-rule to the number of apps in a project. Whether to make code management simpler or delegate app work to a team, the purpose of Django apps is to group application functionality to make work easier.

Django apps are normally contained in sub-directories inside a project. This approach makes it easier to use Python references and naming conventions. If the project name is coffeehouse, the functionality of an app named stores is easily referred through Python packages as coffeehouse.stores.

Because apps provide a modular way to group application functionality, it's common for other people or groups to distribute Django apps with popular functionality. For example, if a Django project requires forum functionality, instead of writing a forum app from scratch, you can leverage one of several Django forum apps. The more general purpose the functionality you're looking for, the more likely you'll be able to find a Django app created by a third party.

Note You already worked with Django apps!

You may not have realized it, but in the previous recipe Set up a database for a Django project you already worked with Django apps when you invoked the migrate operation.

By default, all Django projects are enabled with six apps provided by the framework. These apps are django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages and django.contrib.staticfiles. When you triggered the migrate operation, Django created the database models for these pre-installed apps.

Next, lets create a small Django app. Go to the PROJECT_DIR -- where the urls.py and settings.py files are -- and execute the command django-admin startapp about to create an app called about. A sub-directory named about is created containing the app. By default, upon creating an app its sub-directory includes the following files:

Next, open the views.py file and add the contents from listing 4.

Listing 4 - Handler method in view.py

from django.shortcuts import render

def contact(request):
    # Content from request or database extracted here 
    # and passed to the template for display
    return render(request,'about/contact.html')

The contact method in listing 4 -- like all other methods in views.py files -- is a controller method with access to a user's web request. Notice the input for the contact method is called request. Inside this type of method you can access content from a web request (e.g. IP address, session) through the request parameter or access information from a database, so that toward the end you pass this information to a template. If you look at the last line of the contact method, it finishes with a return statement to the Django helper method render. In this case, the render method returns control to the about/contact.html template.

Because the contact method in listing 4 returns control to the template about/contact.html, you'll also need to create a sub-directory called about with a template called contact.html inside your templates directory (i.e. the one defined in the DIRS property of the TEMPLATES variable).

The contact method by itself does nothing, it needs to be called by a url. Listing 5 illustrates how to add a url to the urls.py file linked to the contact method in listing 4.

Listing 5 - Django url for view method

from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView

from coffeehouse.about import views as about_views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$',TemplateView.as_view(template_name='homepage.html')),
    url(r'^about/', about_views.contact),
]

The first thing that's declared in listing 5 is an import statement to gain access to the contact method in listing 4. In this case, because the app is named about and it's under the coffeehouse project it says from coffeehouse.about, followed by import views which gives us access to the app's views.py file where the contact method is located. The import statement ends with as about_views to assign a unique qualifier, which is important if you plan to work with multiple apps (e.g. import statements without the as keyword, such as from coffeehouse.about import views, from coffeehouse.drinks import views and from coffeehouse.stores import views create the same (conflicting) views reference).

The new url definition in listing 5 now changes the regular expression to match requests on the about url directory (e.g.http://localhost:8000/about/) and instead of directing the request to a template, control is given to the about_views.contact method -- where about_views refers to the imported reference described in the previous paragraph.

Next, start the Django development web server and open a browser on the address http://127.0.0.1:8000/about/. Notice how a request on the about url directory displays the underlying about/contact.html template defined in the contact method in views.py.

Finally, although you can now access an app's views.py methods, you also need to configure the app inside a project's settings.py file. This last step is important so Django can find other app constructs you create later (e.g. database model definitions, static resources, custom template tags)

Open the Django project's settings.py file and look for the INSTALLED_APPS variable. You'll see a series of apps already defined on the INSTALLED_APPS. Notice how the installed apps belong to the django.contrib package, this means they're provided by the Django framework itself. Add the coffeehouse.about app to the list as illustrated in line 8 of listing 6.

Listing 6 - Add app to INSTALLED_APPS in Django settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'coffeehouse.about',
]

As illustrated in line 8 of listing 6, to add apps to a project you add the app package as a string to the INSTALLED_APPS variable. Though the coffeehouse.about app is still practically empty, adding the app to the INSTALLED_APPS variable is an important configuration step for future actions, such as database operations and static resources associated with the app, among other things.