Adding pages to your TOM
------------------------
The TOM Toolkit provides many views (pages) by default, but at some point you may
want to add pages of your own. These could be simple static pages like project or
grant information. Or they can be fully dynamic, displaying data from the database
and containing forms of their own.
In this tutorial we'll start out by adding a simple "About" page to our TOM. Then
to spice it up a little we'll add some dynamic info to the page (a list of
targets). Finally we'll learn how Django can help us create even more interactive
pages.
### A simple template page
Let's get started with some code and we'll explain it piece by piece afterwards.
First, let's create a new file `about.html` and place it in the `templates/`
directory at the root of our TOM. This file will contain the content of our new
page.
```html
To know that we know what we know, and to know that we do not know
what we do not know, that is true knowledge.
Nicolaus Copernicus
```
Next we need to tell Django about this new page and what url to serve it from.
Open the `urls.py` file (next to `settings.py`) and modify it so that it looks
something like this (you may have additional urls already, the important part is
the one relevant to `about.html`):
```python
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('', include('tom_common.urls')),
path('about/', TemplateView.as_view(template_name='about.html'), name='about')
]
```
Notice the `path` function we use here. It takes three arguments. Argument one is
the path in which this page should be made available in our TOM. In this case,
we used the sensible path "about/". The second argument is the view function.
In this case we passed in a
[TemplateView](https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#templateview)
. We'll talk about view functions a bit later, but just know that this class
simply takes the template it should render and renders it. The last argument is
the name of the url. This is so we can refer to this path elsewhere in the
application without the need to hardcode urls.
Enough techno blabber. Launch your TOM and navigate to
[/about/](http://127.0.0.1:8000/about/). You should see something like this:
![](/_static/adding_pages_doc/quote.png)
That's progress, but our new page is pretty ugly. The navigation bar is missing
and we don't have any of the nice CSS that makes the rest of the TOM pages look
good! But wait, before you start copying in lines of HTML, know that all we need
to do is extend
[tom\_common/base.html](https://github.com/TOMToolkit/tom_base/blob/master/tom_common/templates/tom_common/base.html)
to get all that back. You can read more about extending templates from the guide
on [Customizing TOM Templates](/customization/customize_templates). Let's modify
`about.html` to extend the base template:
```html
{% extends 'tom_common/base.html' %}
{% block content %}
To know that we know what we know, and to know that we do not know
what we do not know, that is true knowledge.
Nicolaus Copernicus
{% endblock %}
```
Now when you reload the page you should see this:
![](/_static/adding_pages_doc/base.png)
Much better! By extending a template and providing a `content` block, we are able
to make consistent looking pages without copying and pasting any code.
You can read more about template inheritance in [Django's official
docs](https://docs.djangoproject.com/en/2.2/ref/templates/language/#template-inheritance)
### Adding in dynamic data
We now know how to add basic static pages. But what if we want to show data from
our database? Let's try adding a list of all the targets in our TOM to the about
page. This is slightly more complex, so we're going to create a new file,
`views.py` alongside our `urls.py` file. Add the following content:
```python
from django.views.generic import TemplateView
from tom_observations.models import Target
class AboutView(TemplateView):
template_name = 'about.html'
def get_context_data(self, **kwargs):
return {'targets': Target.objects.all()}
```
Notice we are still using the `TemplateView` here. The only addition is that we
are implementing `get_context_data` which returns a dictionary of data that should
be available to our template. In this case, we are returning all the targets in
our TOM.
Let's modify our `urls.py` to use our new view:
```python
from django.urls import path, include
from .views import AboutView
urlpatterns = [
path('', include('tom_common.urls')),
path('about/', AboutView.as_view(), name='about')
]
```
We've replaced the import of `TemplateView` with an import of the view class we
just wrote, and modified the call to `path()` accordingly.
Lastly let's update our `about.html` template to actually show the list of
targets:
```html
{% extends 'tom_common/base.html' %}
{% block content %}
To know that we know what we know, and to know that we do not know
what we do not know, that is true knowledge.
Nicolaus Copernicus
{% for target in targets %}
- {{ target.name }}
{% endfor %}
{% endblock %}
```
`targets` in this template refers to the key in the dictionary we returned in the
`get_context_data` method in our view. We can add anything to the context
dictionary and have access to it in our templates. In this particular example, we're
iterating over all of the targets in our TOM and displaying all of their names. If you
don't see anything, make sure you have targets in your TOM!
Reloading your about page, you should now see something like this:
![](/_static/adding_pages_doc/targets.png)
If the page looks exactly the same as last time, you might need to add some
targets. Navigate to
[http://localhost:8000/targets/](http://cygnus.lco.gtn:8000/targets/) to do so.
### Class based views
Django has the concept of [class based
views](https://docs.djangoproject.com/en/2.2/topics/class-based-views/intro/).
These classes do one job: they take in an HTTP request and return a response. In
this tutorial we took advantage of Django's
[TemplateView](https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#templateview)
which does a simple job of rendering templates. Django has [many more built in
class based
views](https://docs.djangoproject.com/en/2.2/topics/class-based-views/generic-display/)
that can be taken advantage of. For example, instead of using the `TemplateView`
for rendering a list of Targets, we could have used the
[ListView](https://docs.djangoproject.com/en/2.2/topics/class-based-views/generic-display/#generic-views-of-objects)
which provides additional functionality, such as pagination and filtering.
When working with class based views, you'll almost always subclass them. We did
this with our `AboutView` earlier, and changed the `TemplateView`'s behavior to include a
list of our targets. Herein lies the power of class based views. You can even subclass
the views that ship with the TOM Toolkit itself. So for example, if you don't like
how the
[TargetListView](https://github.com/TOMToolkit/tom_base/blob/15870172e842bcbac17bd4a4b71c9e016b270cf9/tom_targets/views.py#L29)
in the base TOM Toolkit behaves, you could subclass it in your TOM:
```python
from tom_targets.views import TargetListView
class MyCustomTargetListView(TargetListView):
template_name = 'mysupertargetlist.html'
paginate_by = 100
```
### Wrapping it all up
In this tutorial we learned how to not only add static pages to our TOM, but also
how to display some information from our database. Along the way we learned about
Django's [class based
views](https://docs.djangoproject.com/en/2.2/topics/class-based-views/intro/) as
well as some of the things we could use them for.
We didn't get into how to display forms or receive other parameters in our views,
but some [light reading the Django docs](https://docs.djangoproject.com/en/2.2/intro/tutorial04/#write-a-simple-form)
could familiarize one with those concepts.