Allows for an internal link markup to be saved inside markdown-formatted Textfield entries. The links can be used alone, or inside a markdown-formatted link:
{{film:alien-1979}}
{{person:douglas-trumbull}}
[The first Alien movie]({{film:alien-1979}})
…will become (at least on my website), respectively:
[*Alien* (1979)](/cinedex/film/alien-1979)
[Douglas Trumbull](/cinedex/person/douglas-trumbull)
[The first Alien movie](/cinedex/film/alien-1979)
Using the filter, the link is only looked up at display time, so if your view’s URL has changed, that should automatically update with the reverse()
lookup.
You could tweak the regex pattern to match whatever link markup you prefer. I also use Markdown to process my description fields, so I make the link return a markdown-formatted link instead of HTML, but you could tweak that too. If you use Markdown, you’d want to put this filter first.
So to display a description TextField with internal links, in the template would be something like this:
{{ entity.description|internal_links|markdown }}
(See the Django docs on writing your own custom filters for more details on writing and registering filters.)
Written for my movie podcast website, The Optical, and a basic version was shared as the answer to this Stack Overflow question.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
from django import template from django.core.urlresolvers import reverse from my.views import * register = template.Library() @register.filter def internal_links(value): """ Takes a markdown textfield, and searches for internal links in the format: {{film:alien-1979}} ...where "film" is the designation for a model, and "alien-1979" is the slug for a given object NOTE: Process BEFORE markdown If it is inside a markdown link, it will resolve with the link text as intended: [the first Alien movie]({{film:alien-1979}}) [the first Alien movie](/cinedex/film/alien-1979/) If it is by itself, it will resolve to a linked name: {{film:alien-1979}} [Alien (1979)](/cinedex/film/alien-1979/) :param value: :return: """ try: import re # Pattern(s) inside a markdown link first # e.g. [link text here]({{film:alien-1979}}) pattern = '\[.+\]\({{\S+:\S+}}\)' p = re.compile(pattern) text_linked = p.sub(localurl_markdown, value) # After we replace those, find pattern(s) by itself # e.g. {{film:alien-1979}} pattern = '{{\S+:\S+}}' p = re.compile(pattern) #replace the captured pattern(s) with the new markdown link return p.sub(localurl, text_linked) except: # Link lookups fail individually, but just in case there's # some massive failure, just display the original text return value def localurlpattern(string): # Strip off the {{ and }} string = string[2:-2] # Separate the link type and the slug link_type, link_slug = string.split(":") # figure out what view we need to display for the link_type # Dictionary contains lookup as - link_type: viewname # "viewname can be a string containing the Python path to the view object, a URL pattern name, or the callable view object." # see https://docs.djangoproject.com/en/dev/ref/urlresolvers/#django.core.urlresolvers.reverse link_types_views = { 'film': 'film_detail', 'person': 'person_detail', 'company': 'company_detail', 'term': 'term_detail', } # TODO: Maybe add error handling for a bad link_type, and try to look it up anyway link_url = reverse(link_types_views[link_type], args=(link_slug,)) entity = get_object_or_404(Entity, slug=link_slug) if link_type == 'film': # If it's a film, the name should be in italics and include the year. link_name = "*" + entity.name + "* (" + str(entity.release_date.year) + ")" else: link_name = entity.name # Return name and link_url as part of a dictionary link_dict = {'name': link_name, 'url': link_url} return link_dict def localurl(match): string = match.group() try: link_dict = localurlpattern(string) markdown_link = "[" + link_dict['name'] + "](" + link_dict['url'] + ")" return markdown_link except: # The lookup has failed, so let's send back a notice that it's broken print('Broken internal_links localurl to ' + string) markdown_link = "[***[broken link to " + string[2:-2] + "]***](#" + string[2:-2] + ")" return markdown_link def localurl_markdown(match): string = match.group() markdown_link = "" # Grab the link text and link pattern p_obj = re.search(r'\[(.+)\]\(({{\S+:\S+}})\)', string) try: if p_obj: link_dict = localurlpattern(p_obj.group(2)) markdown_link = "[" + p_obj.group(1) + "](" + link_dict['url'] + ")" return markdown_link except: # The lookup has failed, so let's send back a notice that it's broken print('Broken internal_links localurl_markdown to ' + string) if p_obj: markdown_link = "[" + p_obj.group(1) + " ***[broken link to " + p_obj.group(2)[2:-2] + "]***](#" + p_obj.group(2)[2:-2] + ")" return markdown_link else: return string |