Override existing Django Template Tags
Asked Answered
G

7

21

Is it possible to override an existing Django Template Tag? or is it necessary to customize the template file and create a new Template Tag?

Grous answered 4/11, 2012 at 22:33 Comment(1)
E
2

Yes.

As django is basically a python library (as with everything in python) you can over-write anything you'd like.

It's not clear exactly what you'd like to do but it really is pretty easy to roll-your-own templatetag, the docs are pretty clear: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags

This is crazy basic but this is the kind of template I use to start building a custom template tag from:

myapp/templatetags/my_custom_tags.py (must have __init__.py in this directory)

from django import template
register = template.Library()

class CustomTag(template.Node):
    def render(self, context):
        context['my_custom_tag_context'] = "this is my custom tag, yeah"
        return ''

@register.tag(name='get_custom_tag')
def get_custom_tag(parser, token):
    return CustomTag()

Usage in your template goes like this:

{% load my_custom_tags %}
{% get_custom_tag %}
{{my_custom_tag_context}}

You probably want to parse the token, and you probably want some kind of __init__ in the class, but it does go to show how basic it is.


You can browse through the existing 'default' templatetags, replicate and modify them to your heart's content.

There really is some great stuff there: https://github.com/django/django/blob/master/django/template/defaulttags.py

Explosion answered 5/11, 2012 at 9:43 Comment(1)
Well, this example shows how to create a new Tag, but how is it possible to override this existing tag in a separate py-file? I dont wanna touch the template file when it isnt necessary.Grous
C
23

I was looking for the same answer, so figured I'd share my solution here. I wanted to override the default url template tag in django without having to use a custom template tag and load it in every template file.

The goal was to replace %20 (spaces) with + (pluses). Here's what I came up with...

In __init__.py

from django.template.defaulttags import URLNode

old_render = URLNode.render
def new_render(cls, context):
  """ Override existing url method to use pluses instead of spaces
  """
  return old_render(cls, context).replace("%20", "+")
URLNode.render = new_render

This page was useful https://github.com/django/django/blob/master/django/template/defaulttags.py

Cupule answered 15/11, 2013 at 20:15 Comment(2)
for edification, this is very similar to rails alias_method_chain functionality minus the "without" semantics.Cupule
@Cupule hello, in which "init.py" did you do this?Boxwood
D
10

I assume by "an Existing Django Template Tag" you mean a tag in a different app.

Create a templatetags/tagfile.py that registers a tag with the same name. Make sure that tagfile is the same name that the template loads with {% load tagfile %} for getting the original tag.

Also, make sure your app is listed after the original app in INSTALLED_APPS.

Dirkdirks answered 8/10, 2013 at 14:54 Comment(3)
A similar idea: you can load in the register object from the other library (the other library should have something like register = template.Library() and then it registers tags by doing @register.tag etc. You can import register instead of instantiating your own, and do @register.tag(tag_name='existing_tag')Intuit
This is probably the best solution!Claire
...unfortunately it gives me the warning (templates.E003) 'admin_list' is used for multiple template tag modules: 'myapp.templatetags.admin_list', 'someapp.templatetags.admin_list'.Few
D
5

I am pretty sure you are asking for complete override of a django templatetag.

The short answer is - Yes, you can override an existing templatetag.

Here is how to achieve it:

  • You have to have your template directory included in the settings:
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'your_app/templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                    'django.template.context_processors.static',
                ],
            },
        },
    ]
  • You have to include the app you want to override a templatetag for, in the INSTALLED_APPS:
INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'your_app_name',
        ...
    ]

The important bit is to have your app after django's apps !

This is due to the way django works. We will get use right of that.

  • Now, create a folder inside your app, named templatetags. It is important to have __init__.py file inside the templatetags, so django could understand it is a python package!:

your_app_name/templatetags/__init__.py.

  • Create the templatetag you want to override. In that example, i'll use the admin_list.py tag.

In that case, it should be placed like this:

your_app_name/templatetags/admin_list.py

  • Now copy the entire content of the admin_list.py (VERY IMPORTANT!) from django.contrib.admin.templatetags.admin_list.py and modify whatever you want.

It is important to have the whole content of the django's admin admin_list.py and not only a piece of code, else it would not work!


How it works: Django is looking in your application for templatetags folder and uses the template tags inside of it. It places yours template tags after the admin's ones and in short - it overrides them since they are placed after django.admin in the INSTALLED_APPS.

Do not forget to:

  • ./manage.py collectstatic
  • set DEBUG = False

in production.

I have tested it already for result_list(cl) function override, and it is working.


This solution works event without custom html template files.

Hope that helps.

Distressful answered 9/4, 2019 at 16:25 Comment(1)
No way to "extend" the existing templatetag file, similar to how we do with Django templates?Flinty
E
2

Yes.

As django is basically a python library (as with everything in python) you can over-write anything you'd like.

It's not clear exactly what you'd like to do but it really is pretty easy to roll-your-own templatetag, the docs are pretty clear: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags

This is crazy basic but this is the kind of template I use to start building a custom template tag from:

myapp/templatetags/my_custom_tags.py (must have __init__.py in this directory)

from django import template
register = template.Library()

class CustomTag(template.Node):
    def render(self, context):
        context['my_custom_tag_context'] = "this is my custom tag, yeah"
        return ''

@register.tag(name='get_custom_tag')
def get_custom_tag(parser, token):
    return CustomTag()

Usage in your template goes like this:

{% load my_custom_tags %}
{% get_custom_tag %}
{{my_custom_tag_context}}

You probably want to parse the token, and you probably want some kind of __init__ in the class, but it does go to show how basic it is.


You can browse through the existing 'default' templatetags, replicate and modify them to your heart's content.

There really is some great stuff there: https://github.com/django/django/blob/master/django/template/defaulttags.py

Explosion answered 5/11, 2012 at 9:43 Comment(1)
Well, this example shows how to create a new Tag, but how is it possible to override this existing tag in a separate py-file? I dont wanna touch the template file when it isnt necessary.Grous
T
1

If you don't want to depend on the order of your app inside the settings.py INSTALLED_APPS, then you can try the following:

Create the templatetag function/class as usual. Say you want to override a templatetag named otherapp_tags.current_time from an app named other_app. First, create your own version of that function/class:

def my_current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

then, instead of registering this function/class inside YOUR apps namespace, patch the existing function from the other app:

from other_app.templatetags import otherapp_tags

otherapp_tags.register.tags['current_time'] = my_current_time

Typically you should do this inside your AppConfig's ready() method.

Thrill answered 25/1, 2019 at 15:39 Comment(0)
F
1

The actually most voted solution has one major drawback for me - you need to redefine all tags. For some projects, that can be a big drawback. How to solve that? As mentioned by m_floer, it's best to import the register instance from the outside module. So how it should look? Code was taken from Django Jazzmin:

What I want to do? Overwrite the only one template tag: get_side_menu tag.
The tag is located in jazzmin.templatetags.jazzmin, so in our app we will create module templatetags with file jazzmin.py. And the contents will be:

...
from jazzmin.templatetags.jazzmin import register


@register.simple_tag(takes_context=True, name="get_side_menu")
def get_side_menu(context: Context, using: str = "available_apps") -> List[Dict]:
    ...  # your template tag code

This will override just and only selected Django template tag in the easiest way possible. Be aware, that it's for all apps in the given project!

Flan answered 18/4, 2022 at 16:19 Comment(0)
P
0

You can override Django Template Tags. *You can see my answer explaining how to create Django Template Tags.

First, create templatetags folder with __init__.py(Empty file) and custom_tags.py in core folder where settings.py is as shown below, then don't forget to restart server to apply custom_tags.py to Django project. *Other names are fine for custom_tags.py and you can see my answer explaining templatetags folder and load tag:

django-project
 |-core
 |  |-settings.py
 |  └-templatetags # Here
 |     |-__init__.py
 |     └-custom_tags.py
 |-templates
 |  └-index.html
 |-app1
 └-app2

And, don't forget to set core to INSTALLED_APPS in settings.py to apply custom_tags.py to Django project as shown below. *Setting core to INSTALLED_APPS also can collect the static files in core folder to the root Django Project folder with python manage.py collectstatic. I recommend to set core to INSTALLED_APPS before you start building your Django Project:

# "core/settings.py"

INSTALLED_APPS = [
    # ...
    'core', # Here
    'app1',
    'app2',
]

Next, override comment tag in custom_tags.py as shown below. *You can see the original comment tag in /django/template/defaulttags.py:

# "custom_tags.py"

from django.template import Library, Node

register = Library()

@register.tag
def comment(parser, token):
    parser.skip_past('endcomment')
    return CommentNode()

class CommentNode(Node):
    def render(self, context):
        return 'This is not a comment.' # This part is changed.

Then, use the overridden comment tag as shown below:

# "templates/index.html"

{% load custom_tags %}

{% comment %}{% endcomment %}

Then, this below is displayed:

This is not a comment.
Pheasant answered 15/5, 2023 at 8:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.