Manipulating HTML in a Django field // 1-minute guide 🐱📦

This short example shows how to store HTML in a model’s field, alter it and and show on page as processed HTML code.

Storing

You may use Charfield or Textfield, though the latter is preferaby. Foe example, django-tinymce‘s HTMLfield is basically a Textfield anyway.

Manipulation

I will be using Beautiful Soup 4 for this.

Task

In a text, look for the content in certain types of tags (e.g. <u>) and find instances with the text from this tags. Add something to the original content of <u> and remove the <u>‘s themselves.

Source

For example, there are phrases and I want to insert <a> tag with phrases’ links alongside each of them.

Phrases

The world was on fire and no one could save me but you.

Something 2

It’s strange what desire will make foolish people do

Phrases are from Wicked Game by Chris Isaak, courtesy of his recent performance 🙂

Source

Result

Phrases

The world was on fire and no one could save me but you. (link)

Something 2

It’s strange what desire will make foolish people do (link)
Result

Method

def add_link_to_html(html_source):
    soup = BeautifulSoup(html_source, "html.parser")
    for unit in soup.find_all("a", {'class': 'katya_link'}):
        unit.decompose()
    phrases = set(soup.findAll("u"))
    html_source = str(soup)
    result = html_source.replace('<u>','').replace('</u>','').strip().replace('\n', ' ').replace('\r', '')
    for index, original in enumerate(phrases):
        just_text = str(original.get_text()).strip()
        original_str = str(original).replace('<u>', '').replace('</u>', '').strip()
        try:
            phrase_item, created = Phrase.objects.get_or_create(name=just_text)
            phrase_item_any_link = phrase_item.any_link
            if phrase_item_any_link:
                linkd_phrase = '<a class="katya_link" href="%s"><img src="/static/image/link.png"></a>%s' % (
                    phrase_item_any_link, original_str)
            else:
                linkd_phrase = original_str
        except Exception as e:
            linkd_phrase = original_str
        result = result.replace(original_str, linkd_phrase)
    print(result)
    return result
  • div.decompose() I remove every existing <a> with class “katya_link” so that I can use this over and over again, e.g. if I change phrases’ links or some phrases and being removed and added and more/less phrases can get their links
  • soup.findAll("u") I will be adding links to whatever is found between <u>‘s, so I will need this till the end.
  • result is made from the source html, but with <u> removed and made in a single line. You can replace line separation with <br>, if needed.
  • just_text and original are what I will look for and what I will add links to
  • result.replace(original_str, linkd_phrase) so after I made a new content linkd_phrase I replace the original I said we will need till the end

Don’t forget that you won’t modify html_source like this:

html_source.replace('<u>', '')

Only this way:

html_source = html_source.replace('<u>', '')

Template

In order to not see plain html:

<h1>Phrases</h1>
<p>The world was on <i>fire</i> and no one could save me but you. (<a href="/phrases/13/link" class="katya_link">link</a>)</p>
<p>Something 2</p>
It's strange what <i>desire</i> will make foolish people do (<a href="/phrases/46/link" class="katya_link">link</a>)

But processed html:

You need to use |safe inside the temple tag. Like this:

{{ html_text | safe }}

Template language in Django is pretty simplistic, what actually fits ‘every has its place’ ideology well. However, there are some built-in tag that would be entertaining to read especially to those who has a bit of experience of working with templates. Here is Django 2.2 built-in tags page, but you can choose a version in the right bottom corner.

References

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.