'Override existing Django Template Tags

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?



Solution 1:[1]

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

Solution 2:[2]

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

Solution 3:[3]

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.

Solution 4:[4]

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.

Solution 5:[5]

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.

Solution 6:[6]

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!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Williams
Solution 2 eyllanesc
Solution 3 naktinis
Solution 4 ????????? ??????
Solution 5 jrief
Solution 6 Pavel Pan?ocha