The above two methods are very easy. They cache for a specific amount of time and then recreate the page or section on the next request after the cache expires. If the page doesn’t need recaching, Django still recaches it, and if the page does need recaching Django will still wait until the cache expires. That’s usually fine: you’ll set cache expiration to be some reasonable amount of time, and five or ten minutes here or there won’t matter.
Sometimes, though, we know exactly when a piece of the page needs to be recached. For example, in our current blog example, past postings probably never change, so we could set the cache value to an extremely high amount—except that when a page does change, we would want the change to take effect quickly. So we end up recaching unchanging information every couple of minutes on the off chance that someday the author makes a correction to their post.
The most likely reason for caching a post in our above code is that linkTopics filter. If we know that the last cache was made after the last topic was changed and the text itself was changed, then we know we don’t need to recreate it. It doesn’t matter if the cache was created two hours ago or two months ago: if there haven’t been any new topics in that time and the text hasn’t changed, we don’t need to recache.
In situations like that, we can perform the cache within our code. At the top of your templatetags/custom.py file, add:
from django.core.cache import cache
from datetime import datetime
If you still have page cacheing middleware, remove it, and also remove the {% cache %} tag from around the post in index.html and post.html. (You can leave it around the video if you still have it there; it won’t affect this example.)
The cache module includes cache.set, for setting the cache, and cache.get, for getting something out of the cache if it exists. In your linkTopics function, add some new code at the top and bottom:
def linkTopics(text, autoescape=None):
#check for precached topicalized text
(cacheStamp, topicalText) = cache.get(text, (None, None))
newStamp = datetime.now()
#if the text hasn't changed we need to check for new or changed topics
if not topicalText or Topic.objects.filter(changed__gte=cacheStamp):
#recreate the cache
#print "Caching topicalization"
if autoescape:
topicalText = conditional_escape(text)
linkTemplate = template.loader.get_template('parts/inline_topic.html')
for topic in Topic.objects.all():
regex = re.compile('\\b(' + topic.title + ')\\b', re.IGNORECASE)
replacement = linkTemplate.render(context=template.Context({'topic': topic}))
topicalText = re.sub(regex, replacement, topicalText, 1)
topicalText = mark_safe(topicalText)
#cache both the time of this cache, and the linked text
#can't timeout never, so timeout in a year instead
cache.set(text, (newStamp, topicalText), 31536000)
return topicalText
linkTopics.needs_autoescape = True
You can uncomment the print line and watch the terminal to see when the function is caching.
The cache.get() method takes two parameters: the “key” for the cache and the default return value. By using the text itself as the key, if the text changes cacheing will be triggered automatically. If there is no matching text in the cache, the default return value gets None for the text, and the current date and time for the cacheStamp.
If no cached topicalText was returned, obviously we need to recreate the topicalText. But we also need to recreate it if there are new topics or if topic titles have changed. So, after “not topicalText” we say “or there are any topics whose changed field is greater than or equal to the cacheStamp”. If topicalText is empty, Python never bothers to run that filter, because it doesn’t need to—it already knows the “if” is True. But if topicalText has something in it, it runs that filter, and if anything at all comes back, we need to create the topicalized text.
Finally, at the end, we always set the cache again with a timeout of 31,536,000 seconds. That’s one year. Unfortunately, Django’s cache does not allow you to cache forever. All you can do is cache for a really long time. In theory, since the cache stamp is continually refreshed a one-year timeout will never be reached except for extremely unpopular postings1.
We cache both the time we started looking at the text and the new topicalized text; for the key, we use the original text2. Thus, if the original text ever changes that will trigger a recache.