Django for ISchoolers: Difference between revisions

imported>Aldeka
imported>Aldeka
Line 891:
 
= Chunk 4 =
 
<source lang="python">$ git checkout master
$ git pull origin master
$ git branch my-chunk-4
$ git checkout my-chunk-4</source>
 
== Django templates 101 ==
 
Okay, we've done a bit with Django's templating language already when we created those templates in the previous chunk. Now let's look at templating a bit more in-depth.
== Hooking up views to our templates ==
 
=== Tags and variables ===
 
Django comes with its own templating language. A template is (mostly) valid HTML, with some special templating tags thrown in to handle dynamic content. Some of those tags you've already seen, and resemble basic Python operators -- e.g. if statements and for loops. Programmatic statements such as these are always found inside block tags -- {% %} -- which separate them from the surrounding HTML.
 
<source lang="python">
<div class="question {% if question.was_published_today %}new-question-highlight{% endif %}"></div>
{% comment %}This tag checks to see if the given question was published today (using the question model's class function). If it was, the div gets an additional class applied to it, 'new-question-highlight'.{% endcomment %}
</source>
 
Additionally, you have variables, which are not programming statements but rather print data that your view fed to the template so that it appears in the HTML code. Variables can have special filters applied to them, which allow you to e.g. lowercase a given piece of text, but that's the extent of their power. These variables are found inside variable tags -- {{ }}.
 
Example:
 
<source lang="python">
<h1>{{ question.text|upper }}</h1>
{% comment %}This makes the text of the question appear inside the <h1> tag, and filters it so that it appears in all uppercase.{% endcomment %}
</source>
 
A reference to Django template filters and tags can be found here: [https://docs.djangoproject.com/en/dev/ref/templates/builtins/ https://docs.djangoproject.com/en/dev/ref/templates/builtins/]
 
Note: One important thing to keep in mind is that the Django templating language is ''NOT'' as powerful as Python or Django proper! There are many functions that Python has that the templating language does not support. For instance, you can't add or subtract within the templating language. Similarly, the templating language cannot access data in the database all on its own -- it has to be passed that data by the view. This is ''on purpose''. You should NOT be doing heavy-duty computing or data slicing and dicing in your templates! That logic should be in your views.py file, or a helper script of some kind. Remember M-V-C separation?
 
=== Template inheritance ===
 
When you make a web app with multiple pages, there are going to be a lot of elements shared by each of the pages. For example, each of the pages should have the same header and navigation UI, and the same footer. However, right now, we have two templates -- index.html and detail.html -- and each of them are entirely separate from the other. If we wanted to add a header to index.html, we'd have to add it separately to detail.html -- and every other template we have. If we ever decided to change our header, we'd be copying and pasting the whole day long. That's silly! And it violates Django's principle of DRY: Don't Repeat Yourself.
 
Fortunately, Django's template language helps us avoid this problem, by supporting ''template inheritance''.
 
* In qandabear/templates/, create a file named base.html. Put this code in the file:
 
<source lang="html4strict">
<!DOCTYPE html>
<html>
<head>
<title>QandaBear</title>
</head>
 
<body>
<div id="header">
<h1>QandaBear</h1>
<span class="subheader">All your questions answered</span>
</div>
 
<div id="content">
{% block content %}
<h2>Welcome to QandaBear!</h2>
{% endblock content %}
</div>
 
<div id="footer">
This web app is powered by Django!
</div>
</body>
</html>
</source>
 
What did we just do here? We made an HTML skeleton for our app including a header area, a content area, and a footer area. We also used the Django 'block' tag to make a block inside our content div, conveniently named 'content'.
 
* Now that we've got this skeleton, let's modify index.html to inherit from this template.
 
<source lang="html4strict">
{% extends "base.html" %}
 
{% block content %}
{% if latest_qs %}
<ul>
{% for question in latest_qs %}
<li><a href="/questions/{{ question.id }}/">{{ question.text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No questions are available.</p>
{% endif %}
{% endblock content %}
</source>
 
What did we just do? We added an 'extends' tag to the top of our template, which tells Django that this template inherits from another one. We also put our code inside of a set of block tags, a block named 'content'. This tells Django, "Overwrite whatever the base template had inside this block and put this code there instead!"
 
Save your work and check out [http://127.0.0.1:8000/questions/ http://127.0.0.1:8000/questions/]! Now your header and footer appear on the page, along with the list of questions inside your content div.
 
(We should modify detail.html to inherit from our base template as well. That's left as an exercise to the reader.)
 
You can have multiple layers of inheritance -- for instance, if you had some pages with a single-column layout and other pages with a two-column layout, you could have a base template, which is inherited by single-column.html and two-column.html, and each of those could be inherited by other templates. Each template can only have one immediate parent, however -- you couldn't tell a template to inherit both from single-column.html *and* two-column.html.
 
By default, calling a block inside a child template will overwrite any content inside that block in the parent template. Sometimes you don't want to do this, though. For instance, if you had all of your javascript code and import statements inside a block, and wanted to some additional javascript that applied to just one template, you wouldn't want to overwrite everything. Fortunately, there's a tag for that, too!
 
* Add "<code>{{ block.super }}</code>" to your index.html file's content block:
 
<source lang="html4strict">
{% block content %}
{{ block.super }}
{% if latest_qs %}
<ul>
{% for question in latest_qs %}
<li><a href="/questions/{{ question.id }}/">{{ question.text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No questions are available.</p>
{% endif %}
{% endblock content %}
</source>
 
* Save your work and check out [http://127.0.0.1:8000/questions/ http://127.0.0.1:8000/questions/]. What did this variable do?
 
* Commit your work.
 
=== Includes ===
 
There's another way that Django enables you to reuse elements: the 'include' tag. Generally speaking, you should use template inheritance for the most common kinds of reuse. Using includes for everything would get pretty messy! However, sometimes you have a widget that you want to use on multiple pages, and there isn't a clean inheritance relationship between them.
 
* In templates/qandabear, create a file called 'subscriber_widget.html'.
 
<source lang="html4strict">
<div class="widget subscriber-widget">
This web app is totally awesome, don't you agree? <a href="#">Subscribe to our newsletter!</a>
</div>
</source>
 
* Edit index.html's content block:
 
<source lang="html4strict">
{% block content %}
{{ block.super }}
{% if latest_qs %}
<ul>
{% for question in latest_qs %}
<li><a href="/questions/{{ question.id }}/">{{ question.text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No questions are available.</p>
{% endif %}
{% include "qandabear/subscriber_widget.html" %}
{% endblock content %}
</source>
 
* Save your work and check out [http://127.0.0.1:8000/questions/ http://127.0.0.1:8000/questions/]. Oh hey, it's our subscriber widget! (Not much of a widget right now, but you get the idea.)
 
* Commit your work.
 
== Oh, CRUD! (plus, ModelForms) ==
Anonymous user