Courant News: Templates


04.03.09 Posted in Courant News by Max

Dis­claimer: For those who don’t know me, I have a propen­sity for writ­ing long-winded posts (and emails), so I apol­o­gize in advance to those not patient enough to wade through them fully. I try to be thor­ough, and I hope such posts are worth your time.

Today’s topic is tem­plat­ing in the Courant News plat­form; specif­i­cally, the phi­los­o­phy behind it and some of the spe­cial or unique features.

Phi­los­o­phy

Courant News is built on the Django web frame­work, and uses its tem­plat­ing sys­tem. Although one can use other tem­plat­ing engines with Django, we strongly believe in the Django tem­plate design phi­los­o­phy. Jeff Croft wrote an excel­lent post on this topic a few years ago. You should go read it before con­tin­u­ing if you are not famil­iar with Django templates.

Most col­lege news orga­ni­za­tions have lim­ited (if any) pro­gram­ming tal­ent in-house, and their efforts should ide­ally be ded­i­cated to doing actual pro­gram­ming and devel­op­ment work. They should not be unnec­es­sar­ily dis­tracted by the need to assist in the imple­men­ta­tion of site design and them­ing. For those orga­ni­za­tions with­out coders, cus­tomiz­ing the look and feel and basic func­tion­al­ity of the site should not require a pro­gram­mer or an exten­sive train­ing process.

I’m not going to explain how to write Django tem­plates, as there are plenty of sources where you can learn about that. Instead I want to focus on some Courant News-specific details. Nor­mal Django tem­plate vari­ables, tags, fil­ters, and inher­i­tance all play a role and can be used in a nor­mal fashion.

Tem­plate Hierarchy

Courant News is designed to allow orga­ni­za­tions to con­trol almost all aspects of their site either using the admin inter­face or through the tem­plate sys­tem. As much as pos­si­ble, we try to avoid cre­at­ing any rea­son for indi­vid­ual orga­ni­za­tions to mod­ify most of the core Python code, espe­cially the views. As a result, the built-in views use a convention-based tem­plate hier­ar­chy sim­i­lar to how Word­press works, but while still giv­ing you the absolute free­dom to devi­ate if you desire.

For exam­ple, let’s take sec­tion pages. Courant News allows you to define as many nest-able sec­tions as your site requires. For exam­ple, the Yale Daily News has a top-level News sec­tion, with Uni­ver­sity, City, Fea­tures, and Sci/Tech sub­sec­tions. Although we don’t use sub-subsections, there is noth­ing pre­vent­ing the repeated nest­ing of sec­tions as needed. Now, when some­one goes to the URL for, say, the Uni­ver­sity News sec­tion, Courant will search for one of these tem­plates and use the first one it finds:

  • /sections/news/university/detailed
  • /sections/news/subsection_detailed
  • /sections/news/detailed
  • /sections/detailed

The flex­i­bil­ity that this affords is incred­i­ble. You can cre­ate just a “/sections/detailed” tem­plate which will deter­mine how all your sec­tions will look, and leave it at that. But if you want a spe­cific sec­tion to have a dif­fer­ent look, you cre­ate a folder with its slug name and a “detailed” tem­plate inside the folder. Voila, Courant will pick it up and you have a cus­tomized sec­tion tem­plate. You can also con­trol how any sub­sec­tions of that sec­tion will look by defin­ing a “subsection_detailed” tem­plate. This is use­ful if you want sub­sec­tions to return to the default style or if you want all sub­sec­tions to have a cer­tain dif­fer­ent styling. And you can con­tinue adding fold­ers for nested sub­sec­tions to meet your spe­cific needs.

A sim­i­lar sys­tem exists for Arti­cles, which is based on a set of “Dis­play Types” that you can define and asso­ciate with each arti­cle. Courant ships with a sin­gle, default “Stan­dard” dis­play type, but you can define addi­tional types to han­dle dif­fer­ent types of arti­cles. For exam­ple, the YDN will have a new “Promi­nent Media” dis­play type, which will show a large image above the arti­cle text (our stan­dard type just puts media in a side­bar).  Issues have an iden­ti­cal dis­play type sys­tem to allow for mul­ti­ple reusable issue dis­play layouts.

Through­out Courant News, the sys­tem will just search for tem­plates placed accord­ing to such con­ven­tions, which makes it easy to cus­tomize spe­cific aspects of a site while hav­ing stan­dard fall­backs and thus allow­ing a min­i­mal of ini­tial setup work and an evolv­ing site over time. Note that you can cre­ate what­ever other tem­plate files you desire, these sets of con­ven­tions are only for choos­ing the ini­tial tem­plate file to load. The YDN has an exten­sive set of sup­port tem­plates con­tain­ing HTML snip­pets that get used in mul­ti­ple places, and we take full advan­tage of tem­plate inher­i­tance as well.

File Exten­sions

Above you may have noticed that the tem­plate search paths did not use any file exten­sions. The rea­son for this is that Courant sup­ports arbi­trary file types and exten­sions in URLs. By default, any URL with­out a file exten­sion will be assumed to be an HTML page, and all tem­plate search paths will get a “.html” appended to them. How­ever, if a user puts .rss or .mobile on the end of a page’s URL, Courant will attempt to use a tem­plate with that file exten­sion. So to cre­ate a mobile ver­sion of the default sec­tion tem­plate, I’d sim­ply cre­ate “/sections/detailed.mobile” in addi­tion to my “/sections/detailed.html” which nor­mally gets used. And like­wise for “/sections/detailed.rss” or any other file exten­sion you might want to use, such as .print. Courant will ship with a base site pack­age that will include tem­plates such as a default sec­tion RSS feed tem­plate, since such things are pretty stan­dard. But if you wanted your sub­sec­tion to have a cus­tom RSS feed, you could do that just as eas­ily using the same con­ven­tion scheme described above.

The power of this sys­tem is that you get imme­di­ate sup­port for arbi­trary file exten­sions with­out requir­ing any code mod­i­fi­ca­tions at all. No need for cus­tom wran­gling of a url con­fig­u­ra­tion file or any­thing complicated.

Get” Tag

One thing that is often dif­fi­cult for peo­ple learn­ing how to use a new tem­plate sys­tem is learn­ing how to fetch the data they require. This often requires learn­ing a maze-like plethora of tem­plate tags or func­tion names.  Early ver­sions of Courant fell into this trap, as there are count­less per­mu­ta­tions of ways that one might want or need to fetch and fil­ter data for dis­play on a news site. The sit­u­a­tion quickly became unten­able, even for us pro­gram­mers.  Our solu­tion was the “get” tem­plate tag, which allows tem­plate authors to fetch data in sentence-like for­mat which is easy to read and under­stand. Explo­ration of the full extent of the get tag will take a long post of its own, but I’ll high­light some inter­est­ing parts here.

Know­ing what data is nec­es­sary is often straight­for­ward, such as the arti­cle for an arti­cle view page, a sec­tion for a given sec­tion page or an issue for a given issue archive page. How­ever, for many pages, espe­cially the all-important home page, each news site chooses to dis­play dif­fer­ent sets of data. Instead of Courant try­ing to guess or tell you what to use, the get tag allows you to get what­ever you need. It is sim­i­larly use­ful in side­bar wid­gets and other such blocks on sites that are inde­pen­dent of the main page focus.

For exam­ple, if we wanted to just list all the arti­cles by sec­tion in the lat­est issue, we might have the fol­low­ing calls in our home­page template:

{% get issue order by -published_at limit 1 as latest_issue %}
 
<h1>{{ issue.name }}</h1>
 
 
{% get sections in issue latest_issue as section_list %}
{% for section in section_list %}
 
<h2>{{ section.name }}</h2>
 
 
{% get articles in section section in issue issue as articles %}
 
<ul>
    {% for article in articles %}
 
	<li>{{ article.heading }}</li>
 
 
{% endfor %}</ul>
 
 
{% endfor %}

This exam­ple just shows the most basic use case, which is fil­ter­ing result sets by their rela­tion­ships to other objects. As shown, the tag sup­ports lim­it­ing the num­ber of results; it can also use an off­set, so if you want 6 arti­cles after the lat­est 5, you could use:

{% get articles in section "University News" limit 6 offset 5 as article_list %}

The tag also sup­ports fil­ter­ing based on date ranges, so that you can get arti­cles from the last week or 17 days, or events from the next 3 weeks.

{% get events from next 3 weeks as upcoming_events %}

Another use­ful bit of func­tion­al­ity is the ‘with’ clause, which enables you to restrict objects by what types of rela­tion­ships they have. For exam­ple, we might want the lat­est 5 arti­cles with attached media for dis­play in a spe­cial carosel box on our homepage.

{% get articles with media limit 5 as latest_articles_with_media %}

You can do sim­i­lar calls to restrict by tag clas­si­fi­ca­tions. There are a num­ber of other bits of syn­tax func­tion­al­ity, but I’ll leave those for another day. The tag will have exten­sive doc­u­men­ta­tion which I will post some­time in the not-too-distant future.

One of the most impor­tant things about the get tag is that it isn’t lim­ited to the con­tent types built-in Courant. If you build your own Django mod­els, you can reg­is­ter them with the get tag, option­ally pro­vid­ing some cus­tom code to han­dle some of the fil­ter­ing logic if nec­es­sary. For exam­ple, the Sec­tion model uses cus­tom code to deter­mine how a string passed as a sec­tion name (such as “Uni­ver­sity News” in the exam­ple above) gets mapped to a sec­tion object, tak­ing into account the nest-ability of sec­tions. Because all Courant built-in mod­els use the same reg­is­tra­tion func­tion as your addi­tional mod­els will, they are not spe­cial in any way and your own code can fit in seamlessly.

from django.db import models
from courant.news.models import Article
from courant.gettag import gettag
 
class CustomArticle(Article):
    custom_field = models.CharField(max_length=100)
gettag.register(CustomArticle)

It’s really that sim­ple. In your tem­plates you could then use

{% get custom_articles in section some_section as articles %}

to get all instances of your Cus­tom­Ar­ti­cle model in a given sec­tion. I’ll have blog posts in the future that describe more about how to write cus­tom arti­cle and media types and how they fit cleanly into the rest of the system.

Con­clu­sion

As I have passed the 1500 word mark, I’m going to wrap this post up. I’ve cov­ered three of the impor­tant fea­tures of the Courant tem­plate sys­tem, and I hope it has served as an insight into the kind of think­ing that went into the phi­los­o­phy and design of Courant News. I’ll have more posts soon going into more detail about these fea­tures, as well as a slew of oth­ers for which I didn’t have space to talk about today.



Leave a Reply