Set up content: Understand urls, templates and apps

Content in 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, which is part of Django's loosely coupled architecture design principles.

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 need to create and configure Django apps, in addition to urls and templates.

But before describing how to create and configure urls, templates and apps, it's very important you understand how each of these parts works with one another. Figure 1-4 shows the Django workflow for user requests and how they work with Django urls, templates and apps.

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

As you can see in figure 1-4, there are two separate pipelines to deliver either static or dynamic content. More importantly, notice how each of the different Django layers is loosely coupled (e.g. you can forgo the apps layer if it isn't required and the urls layer & templates layer are still able to communicate with one another).

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 listing 1-11 earlier in the chapter. If you open the urls.py file, you'll notice it only has one active url to /admin/ which is the Django admin -- I will discuss the Django admin in the next and final section of this chapter.

Now that you're familiar with the urls.py file 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 next chapter includes a dedicated section on the use of regular expression in Django urls. In addition to the regular expression pattern, an action of what to do when a request is intercepted for a 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 django.contrib import admin -- and line 9 -- the one below url(r'^admin/', admin.site.urls), -- as illustrated in listing 1-16.

Listing 1-16. 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 show in listing 1-16, 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 the action TemplateView.as_view(template_name='homepage.html'). This last action is a helper method to direct the requesting party to a template which takes the argument template_name='homepage.html'.

In summary, the url method you added in listing 1-16 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, as I'll describe shortly and extensively in the next chapter.

Now lets 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.

Caution 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 section on setting up a database 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 1-17 into it. Save the file on your system, in a sub-directory called templates in your Django project's PROJECT_DIR.

Listing 1-17. 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 a Django project 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 recommended previously, you should aim to keep Django templates inside a sub-directory -- using an obvious name like templates -- in a Django project's PROJECT_DIR. So for example, if the absolute path to a Django project PROJECT_DIR is /www/STORE/coffeehouse/, the recommended location for a DIRS value would be /www/STORE/coffeehouse/templates/. Listing 1-18 illustrates a sample DIRS definition in settings.py using the PROJECT_DIR reference variable set dynamically at the top of settings.py.

Listing 1-18. 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',
            ],
        },
    },
]

An important take away from listing 1-18 is that it doesn't use hard-coded directory paths, instead it uses the PROJECT_DIR variable which is determined dynamically. This may seem trivial at the moment, but it's a good practice once the location of a Django project has a tendency to change (e.g. group development, deployment to production).

Finally, 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 contents 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 coffeehouse, you can create an app for stores, another app for menu items, 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.

You already worked with Django apps!

You may not have realized it, but in the previous section when you 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:

Next, open the views.py file and add the contents from listing 1-19.

Listing 1-19. Handler view method in views.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 1-19 -- 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) using the request reference 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 1-19 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 1-20 illustrates how to add a url to the urls.py file linked to the contact method in listing 1-19.

Listing 1-20. 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 1-20 is an import statement to gain access to the contact method in listing 1-19. In this case, because the app is named about and it's under the coffeehouse project folder 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. For example, import statements without the as keyword, such as from coffeehouse.about import views, from coffeehouse.items import views or from coffeehouse.stores import views can import conflicting view method references (e.g. three methods named index), so the as qualifier is a safeguard to ensure you don't unintentionally use a method with the same name from another app.

The new url definition in listing 1-20 uses a regular expression to match requests on the about url directory (e.g. http://127.0.0.1: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 1-21.

Listing 1-21. 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 1-21, 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.