Beginner Lesson 7: Dynamic Pages and Navigation


Transcript References
The video mentions copying code from the screen or transcript. This is a reference to the old, paid version of the course. I have now collated all the code together in one place in the source, which you can download here.

In the last module we created our site template and a page template that inherits from the site template. While our project is finally starting to look like a real website, it’s still static—the page template doesn’t show the site content and we still have no site navigation to be able to load different pages on the site.

In this module, we’ll modify the page template to show the selected page content dynamically, and turn the placeholder text in the left menu into navigation links to our site pages.

To achieve this outcome, we have four tasks to complete:

  1. Modify our URLs to capture a page link;
  2. Rewrite our index view to select the correct page and return the content to the template;
  3. Modify our templates to show the new content; and
  4. Turn the placeholder menu list in the left sidebar into a navigation menu.

Modify Page URLs

If you remember from Module 4, we configured our URL dispatcher to load the index view when we navigated to the root URL. If we want to open another page, we need to give Django more information.

Common practice is to create a custom URL for each of our pages, for example, /services will link to the Our Services page. In Module 5, when we created the Pages model we added a field called permalink. The permalink field contains a text string that our Django application will use to match our URL to the correct page.

For Django to know what page we’re requesting, we need to be able to extract the page link from the requested URL. We do this by modifying our file. Pause the video and enter the code into your editor or copy it from your transcript.

I’ve commented out the original URL pattern in line 5 so you can see what’s changed. In line 7, we’re using a capturing group.

Everything inside the angle brackets will be captured and sent to the view as a parameter. The capturing group is basically saying “capture everything after the domain name and send it to the view as the string parameter pagename”.

For example, if the URL /services is passed to this function, the index view is called, and the string “services” is passed to the view in the pagename parameter.

str: is a path converter, which will convert the captured data into a string. Other path converters are available, including int and slug which will convert the captured data to an integer or a text slug respectively.

The default is a string, so str: is not technically necessary but, in sticking with the Zen of Python, it’s better to be explicit.

Because the path() function is not able to capture an empty string, we must create a special case for the home page, which is exactly what we are doing in line 6. When the user navigates to the site root, line 6 will set pagename to an empty string (”).

Now it’s time to rewrite and test the view.

Rewriting the View

We’ve changed our URL dispatcher to be able to capture information from the URL, now we’re going to modify the index view.

Before we start rewriting the view, let’s have a look at how Django works with models to retrieve information from the database.

You can see I’m using the terminal inside Visual Studio Code here, but you could just as easily use PowerShell or the Windows command prompt.

First, we need to run python shell to open Django’s interactive shell. Remember you must be running your virtual environment for this to work.

From Django’s interactive shell, complete the exercise by following along with me in the video. If you get a bit behind just pause the video. I’ve also put a copy of the terminal output in the transcript, so you can use that if you like, just remember if you’re using the transcript you don’t have to type in the lines with the three greater than symbols — this just shows you what Python prints out in your terminal.

  • First, We import the Page model from our pages app.
  • Then we retrieve a single page from the database and store it in the object pg. In this example we are retrieving the home page.
  • Now that we have a page object, we can access its attributes:
    • Page title
    • Page last update date
    • and the page content which is stored in the body text field of our Page model

We can now use what we learned in the interactive shell to create a new index view. Pause the video and enter the code into your editor or copy it from your transcript.

Except for the first line, this is all new code, so let’s go through it in detail:

  • In Line 3 we import the Page model into the view.
  • In Line 5 we add the pagename parameter to the definition of the index view. Remember from the last lesson that the string captured by the URL dispatcher will be assigned to pagename when the view loads.
  • Line 6 is needed as Django removes the slash from the front of our URLs. If we don’t prepend a forward slash to pagename, the URL links in our template will be relative to the current page, not relative to the root.
  • In Line 7 we’re using the same Page.objects.get() method we used to load a page when we explored the model in the interactive shell. The pg object will contain the page where the page permalink is equal to the string stored in the pagename parameter.
  • In Lines 8 to 12 we’re using the fields of our pg object to populate a dictionary of items to pass to the template. In Django, this dictionary is called the context. The context variables will be used by the template to render dynamic content to the browser.
  • In line 13 I’ve added an assert statement so we can test the view. We’ll get to this in a minute.
  • And finally, in line 14, we’re using the render() function to render the view. render() requires a request object, the name of the template and the context. Django will then compile the webpage from the information provided, and return an HTTP response to the browser containing your page content.

Testing the View

One simple, but very powerful way of testing to make sure the view is passing the right information back to the template is to use Django’s error page to examine the output of the view. Django’s error page can be triggered by inserting assert False into your code. Uncomment line 13 in your code, run your development server and navigate to the root of your website.

When Django’s error page shows, scroll to the end of the traceback information.

If you click on Local vars, the frame expands to show the information you are passing to the template. In the context dictionary you can see the page variables you are passing to the view.

This ability to look inside your views is supremely useful—if you get into the habit of using Django’s error view in this way, you can dramatically reduce your debugging time when developing more advanced websites.

Now that you have seen how to put the error page to use, it’s time to modify the templates, but don’t forget to comment or delete Line 13 before you move on!

Modify Templates

We have a few changes to make to improve the templates so we’re going to do a bit at a time in this lesson, so you can see the process broken down into simpler steps.

First, we modify the page template to show the page content. Pause the video and enter the code into your editor or copy it from your transcript.

We’ve only changed one line in this template—we’ve replaced the placeholder text in Line 4 with a template variable named content. This variable will contain the page content at runtime.

Save the template and then launch the development server. When you load up the home page, you’ll notice something is wrong—your nicely formatted HTML is showing in one big ugly block!

This is because Django by default auto-escapes any HTML before it’s rendered in a template. I’ve included this to show you something Django does automatically to protect your website from hackers. By auto escaping code, django can stop an attempt to inject executable code into your site.

To stop Django auto-escaping content you want rendered as HTML, you use the autoescape tag. Pause the video and enter the code into your editor or copy it from your transcript.

You can see the changes I’ve made in lines 4 and 6. I’ve surrounded the content variable with an autoescape block so Django doesn’t escape any of our page content. If you reload the page, it should now look great!

Your view should now be working correctly and able to show your site pages. Test it now by trying to navigate to the services and the about pages.

If something goes wrong, remember that the Django error page gives you a lot of useful information for troubleshooting your site.

Improving the Templates

Our website is looking great, but a couple of things still need to be done to the site and page templates before we create the navigation – we need to set the page title; and add a last updated date to the bottom of the page content.

To update the page title for each page, we first need to add a set of block tags to the site template. Pause the video and enter the code into your editor or copy it from your transcript.

This should be straight forward—in lines 7 and 9 we’ve entered an opening and closing block tag and named it “title”.

Now we need to override the new title block tag in our page template. Pause the video and enter the code into your editor or copy it from your transcript.

Line 3 is where the override magic happens—we’re replacing the placeholder text in the site template with the {{ title }} variable, which at runtime will contain the title of our page.

I have also added three new lines to the file. Lines 10 and 12 are simply HTML paragraph opening and closing tags.

More importantly, in line 11 you can see that the last_updated field has been added to a Django variable tag. The additional code after the pipe character is an example of applying a filter to a template tag.

Django’s template language has many filters that do everything from formatting strings to performing minor logical and mathematical operations. In this instance we’re using Django’s date filter to format the last_updated date as a long-form date.

Save the files, run the development server and your browser should display the completed home page, with title in the browser tab and nicely formatted date information at the bottom of the page.

Well done!

Like the Content? Grab the Book for as Little as $5!

Other than providing you with a convenient resource you can print out, or load up on your device, buying the book also helps support this site.

eBook bundle includes PDF, ePub and source and you only pay what you can afford.

Get the eBook bundle here.

You can get the paperback from Amazon.

Create a Menu

Now that we can display our page content dynamically, we need a way to navigate our website.

We do this with a menu. Menu’s can get complex in a modern website—with animation and responsive layouts—however, at their most basic, they are simply a list of links to your content.

In this lesson, we’ll be taking the latter approach—we’ll be implementing a menu in the left sidebar of our site, with the title of the page linked to the page content via the page’s permalink. We want a menu entry for each page, so we will be creating an HTML list of the pages of our site, with the page title as the anchor text and the permalink as its URL.

Before we do that, however, let’s use the interactive shell to have a look how we retrieve a set of objects from the database. Exit the development server and run the Django interactive shell.

Follow along with me as I type commands into the shell. If I’m going too fast for you at any stage, just pause the video until you catch up.

  • First we’re going to import the page model.
  • Next we want to retrieve all our page records from the database
  • In Django, the all() method returns a copy of the current queryset. A queryset is simply a collection of objects that have been returned from the database. In this example, the queryset is made up of page objects. In simple language, this line is saying “retrieve all the pages from the database and put them in the queryset named page_list.
  • A queryset is iterable, meaning you can step through each record in the queryset using Python’s for loop. Each time Python steps through the for loop, it loads the next page into the pg variable.
  • Once pg contains a page object, you can access the permalink and title fields for the page. As the for loop works through the pages in the database, we’re using the print class to print out the permalink and title for each page.

Update the View

Moving over to our project, let’s use what we have learned to add a list of pages to our view context. Pause the video and enter the code into your editor or copy it from your transcript.

I have added one line to here. In line 12 you can see that I have added a variable page_list to the context and populated it with the pages in our database using Page.objects.all() just like we did in the interactive shell.

Modify Templates

Now we need to modify the templates to display the new menu.
First we modify the base template. Pause the video and enter the code into your editor or copy it from your transcript.

Another simple change—In lines 22 and 24 we have entered an opening and closing block tag and named it “sidenav”. Now we need to override the new sidenav block tag in our page template. Pause the video and enter the code into your editor or copy it from your transcript.

I’ve added a whole new block to this file. Note that it doesn’t matter where you put the block in the file, just as long as you don’t put it inside another block. I have put it between the title and content blocks, but you could just as easily put the new block at the end of the file and it still works.

Let’s have a look at what this new code does:

  • Line 6 contains the opening tag for a for loop in Django’s template language. While the syntax is different, it works the same way as Python’s for loop. The closing tag is on line 10.
  • Line 8 is the code for a menu item. It’s a standard HTML anchor with the page permalink as the URL and the page title as the anchor text.
  • Lines 7 and 9 format the anchor as an HTML list item. Remember, the ul tags that define an HTML list are provided by the base.html template, so we don’t have to add them here.

And that’s it—if all has gone to plan, when you start up the development server again, you should have a fully functioning website with your three pages displayed correctly and a navigable left menu displayed.

More Tags and Filters

We have covered only a very small subset of the most common Django template tags and filters in the last couple of lessons. If you want to learn about the more popular template tags and filters, I have provided reference tables in the Appendix of Build a Website With Django 2.

Up Next…

Scroll to Top