Some time ago I've released django-content-bbcode - a BBCode alike tag parser. In this article I'll show some example of usage for such parser - from simple search and replace to more complex using database to get the response.
I've wrote a bit about creating tags for django-content-bbcode on na github. But let us start with basic example. Let say we want to turn this:
Into a clickable URL. The "anchor" is the tag name (rk:NAME), next we have one attribute - "href". We can also have tags with a closing tag:
Such tags also have the inner content parsed. django-content-bbcode parses those two types of tags and passes all parsed data as a dictionary to a function that handles given tag. The function gets a list of dictionaries - a list of all occurrences of given (by name) tag:
For it to work place the code in tags.py file in one of your Django applications (the one using/providing given tag). You will also have create registered_tags dictionary, where key is the tag name and value is the function callable that will handle it. There is an example on github.
Our "anchor" function gets two arguments - a list of dictionaries (occurrences) and text in which those tags were found. Every occurrence dictionary will have "href" attribute. Under special key "tag" there will the raw tag itself. Thanks to that we can replace it in the text with any result we want. In this case we iterate over the list and replace every tag with some HTML code.
Tags with closing tag would also have the inner content under "code" key.
Let say that we want to have linkable headlines in article so that it's possible to give a link to given headline in the article:It can be done with tag like this one:
We replace every tag occurrence with a HTML code we generated. Every occurrence is numbered to provide unique "A" label name. The "id" attribute determines headline size - h1 or smaller. This example has a bad coding style - it's not good to generate code and to mix different things with each other. A better implementation would look like so:It would use a Django template to render the response: No code generation and we splitted handling from response generation.
We can implement even complex tags that will use database or other source to render the result. As a basic example we can implement latest registered users list:
When creating database using tags it's good to think about caching - either at tag function level or in a view or template in which slow rendering tag will be present.
Data such a list of latest registered users, latest news etc. could be implemented in views or in templates with the help of a function from TEMPLATE_CONTEXT_PROCESSORS. The tag based solution gives you the ability to create and modify content and structure of a page as you desire without the need to change something in the code. It's not always needed or desired, but on for example wiki-alike pages it's quite essential to create content structure without coding it. It can also be handy for static page generators that output formatted static pages for use on Github Pages and alike.
We can also run into a problem in which many tags on one page will execute multiple queries. For example a tag that inserts a link and description of an article given by slug. Many tags - many slug queries. We can solve it by fetching all articles before iterating every occurrence:
We gather all slugs and then make one query using "IN" operator. It's also recommended to see if the code needs also some fields in select_related. The "IN" queries can be helpfull if the list isn't very big. Also don't give it a queryset but flat IDs list as it may end up in a monster query with subselects executed multiple times. Also note that this implementation has some code generation that could be done better ;)
We can also use some external packages like pygments for colour highlighting or pillow for thumbnail creation. For example code highlighting would look like so:
Aside of hackerish CSS injection (they could also be added to site CSS globally) the function looks similar to others. The "lang" attribute determines language for highlighting and the "code" tag has the code to be highlighted.
You can also change the function implementation, like change the library used for highlighting without the need to apply changes to every text in which the tag is used.