iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article

Lorem Ipsum in Django?

に公開

Introduction

I'm Naito (@engineer_naito).

I'm currently writing an article about Django.
While researching for that article, I came across the {% lorem %} template tag.
In this article, I will dive deep into this feature.

What is lorem ipsum?

https://en.wikipedia.org/wiki/Lorem_ipsum

In publishing and graphic design, Lorem ipsum (/ˌlɔː.rəm ˈɪp.səm/) is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content.

It is a placeholder (dummy text) used to verify the display of text and other elements.
lorem ipsum is a sequence of words derived from Latin that has no meaning. (Apparently, such text is called "greeking.")

When creating design prototypes, dummy text is often used to check and adjust the appearance of the text when the formal content is not yet complete.

https://www.lipsum.com/
At this site, you can generate lorem ipsum.
You can specify the number of paragraphs, words, characters, and lists(?).

https://www.lipsum.com/

https://loripsum.net/
This site provides a Web API for generating lorem ipsum.

https://loripsum.net/

Django's lorem ipsum

Django's Built-in Template Tags

https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#built-in-tag-reference
As described in the link above, Django has many built-in template tags.

  • if statement (conditional branching): {% if %}
  • for loop (repetition): {% for %}
  • csrf protection: {% csrf_token %}
  • template inheritance: {% extends %}

Many useful (perhaps essential?) tags are provided.

Template tags are expressed on templates enclosed in {% %}, and you can also create your own.
However, since many features are provided built-in, I don't think there are many cases where you need to define your own template tags.

Me: "Is there a {% lorem %} too?"

I was trying to prepare an example of a custom Django template tag and was thinking about various ideas.
Searching on Google or asking interactive AI didn't give me very good advice.

My first idea was to create a custom tag to display the rendering time.
However, it already existed as a built-in tag.
https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#now

{% now "F jS Y H:i" %}

You can specify the format like this,

February 9th 2024 09:00

and it will be rendered as shown above.

My next idea was lorem ipsum.
When I was using Django for work, I used to create dummy data manually.
(If I needed 50 characters, I would copy and paste "A" 50 times.)

I didn't know.
I didn't know that Django had lorem ipsum.
https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#lorem

{% lorem %}

How to use {% lorem %}

The lorem tag takes three optional arguments.

{% lorem [count] [method] [random] %}
  • count
    You can specify the number of paragraphs or words to generate.
    (Default is 1.)

  • method
    Three types of characters, w, p, and b, are allowed.
    (Default is b.)

    • w: Words
    • p: HTML <p> tag paragraphs
    • b: Plain text paragraphs
  • random
    By specifying random, it will output random Latin words.

Examples of {% lorem %}

<!-- First 5 words of Lorem Ipsum -->
{% lorem 5 w %}

<!-- 2 paragraphs of random Latin (2 <p> elements) -->
{% lorem 2 p random %}

<!-- First 2 paragraphs of Lorem Ipsum (concatenated into a single text) -->
{% lorem 2 b %}

Implementation of {% lorem %}

I was a bit disappointed that my second idea fell through, but then I became curious about how {% lorem %} is implemented.
Does it call an external Web API?
Let's actually check the source code on GitHub.
For no particular reason, I'll look at the stable/5.0.x branch, which seems to be the latest.

https://github.com/django

The definition of lorem can be found in:
https://github.com/django/django/blob/stable/5.0.x/django/template/defaulttags.py
(https://github.com/django/django/blob/222bf2932b55ebc964ffc5f9a6f47bad083e5ac2/django/template/defaulttags.py#L1102-L1146)

django/django/template/defaulttags.py
@register.tag
def lorem(parser, token):
    """Docstring citation omitted"""
    bits = list(token.split_contents())
    tagname = bits[0]
    # Random bit
    common = bits[-1] != "random"
    if not common:
        bits.pop()
    # Method bit
    if bits[-1] in ("w", "p", "b"):
        method = bits.pop()
    else:
        method = "b"
    # Count bit
    if len(bits) > 1:
        count = bits.pop()
    else:
        count = "1"
    count = parser.compile_filter(count)
    if len(bits) != 1:
        raise TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return LoremNode(count, method, common) 

It receives the content of the template tag itself as a token.
(We'll ignore the parser for now.)

 {% my_tag 'arg1' 'arg2' %}

For a tag like the one above, the token is a string: "my_tag 'arg1' 'arg2'".
It truly receives the tag contents as they are.
The string is split by split_contents() and stored in a list called bits.
(['my_tag', 'arg1', 'arg2'].)

Example:
{% lorem 2 w random %}
-> ['lorem', '2', 'w', 'random']

bits[0] is assigned to tagname ('lorem').
Whether bits[-1] is 'random' or not is assigned to common.

After that, it continues to look through the elements of bits from the end in order, and finally returns LoremNode(count, method, common).

LoremNode is defined in the same file.
(https://github.com/django/django/blob/222bf2932b55ebc964ffc5f9a6f47bad083e5ac2/django/template/defaulttags.py#L332-L349)

django/django/template/defaulttags.py
class LoremNode(Node):
    def __init__(self, count, method, common):
        self.count = count
        self.method = method
        self.common = common

    def render(self, context):
        try:
            count = int(self.count.resolve(context))
        except (ValueError, TypeError):
            count = 1
        if self.method == "w":
            return words(count, common=self.common)
        else:
            paras = paragraphs(count, common=self.common)
        if self.method == "p":
            paras = ["<p>%s</p>" % p for p in paras]
        return "\n\n".join(paras)

words and paras are imported from their respective modules:

django/django/template/defaulttags.py
from django.utils.lorem_ipsum import paragraphs, words

Let's take a look at utils/lorem_ipsum.py.
Finally, we get to know the secret(?) of the lorem ipsum implementation in Django.

django/utils/lorem_ipsum.py
COMMON_P = (
    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
    "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
    "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
    "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
    "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
    "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
    "mollit anim id est laborum."
)

It seems that the paragraphs and words for lorem ipsum are defined internally.
(COMMON_P, WORDS, COMMON_WORDS)

It doesn't look like it's calling an external Web API.

Summary

Django's built-in template tag {% lorem %} does not call an external API.
It defines strings as constants and generates the required elements from those constants based on the objective.

Finally

Django is a technology I have used for over a year professionally, but I had hardly ever concerned myself with its implementation until now.
I thought that reading the code of the framework you are using is also quite enjoyable.

Thank you for reading to the end!

Discussion