Opened 9 years ago

Last modified 21 months ago

#2539 new New feature

Custom tags and filters can be restricted by namespace

Reported by: limodou@… Owned by:
Component: Template system Version:
Severity: Normal Keywords: djangocon
Cc: me@…, ramusus@…, mhf@… Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

How to use namespace? For example,

{% load example %}
{% example.testtag %}

And I think if the namespace can be optional is better. I don't know
if it's useful, but sometimes I read others projects, and as I reading
the template, I don't know which tag is in which file. So find the
correct tag definition file is somewhat diffcult. If I can add
namespace in the front of a custom tag name, it'll be easy to locate
the source file.

The maillist discussion thread is HERE

Attachments (6)

template.diff (5.6 KB) - added by limodou 9 years ago.
template_01.diff (5.6 KB) - added by limodou@… 9 years ago.
Fix regular pattern [\w.]+ to [\w\.]+
template_02.diff (5.7 KB) - added by limodou 9 years ago.
move rsplit to rindex and reduce duplicate path entry in path
template_03.diff (5.7 KB) - added by limodou 9 years ago.
change rsplit to rindex. (upload again, last patch disappeared)
template_04.diff (5.5 KB) - added by racter 7 years ago.
adjusted limodou's patch to apply to trunk as of 6213
patch_2539_code_tests.diff (17.4 KB) - added by durdinator 7 years ago.
Patch against [6589], including tests and docs

Download all attachments as: .zip

Change History (38)

Changed 9 years ago by limodou

comment:1 Changed 9 years ago by limodou@…

  • Summary changed from Can custom tag use namespace to [Patch]Custom tags and filters can be restricted by namespace

This patch include three modified files. So you can load a custom taglib in two styles in template file:

{% load example %}
{% load testapp.example %}

Then you can use tags and filters through three styles, for example:

{% testtag %}    <---- current usage
{% example.testtag %}
{% testapp.example.testtag %}

and you can also apply this style to filter, for example:

{{ "string"|testfilter }}
{{ "string"|example.testfilter }}
{{ "string"|testapp.example.testfilter }}

comment:2 Changed 9 years ago by limodou@…

I forgot that you can also use:

{% load testapp1.example %}
{% load testapp2.example %}

So you will see you can load homonymy taglibs distinquished by app name with this patch. And if there are homonymy tags, and you didn't restrict them with namespace, the last one will be effective. But you can restrict them by namespace, just like:

{% testapp1.example.tag1 %}
{% testapp2.example.tag2 %}

So this time, each tag will be different.

comment:3 Changed 9 years ago by anonymous

line 507: (?P<filter_name>[\w.]+)
should be (?P<filter_name>[\w\.]+)
right? otherwise you'll include any character, not just dots.

comment:4 Changed 9 years ago by limodou

Right. I'll change it.

Changed 9 years ago by limodou@…

Fix regular pattern [\w.]+ to [\w\.]+

comment:5 Changed 9 years ago by Tom Tobin <korpios@…>

That's no so, anonymous:

[] Used to indicate a set of characters. Characters can be listed individually, or a range of characters can be indicated by giving two characters and separating them by a "-". Special characters are not active inside sets.

(Emphasis mine; from http://docs.python.org/lib/re-syntax.html)

limodou, there's no need for that patch. :)

comment:6 Changed 9 years ago by Tom Tobin <korpios@…>

Err, I should say there's no need for that patched patch. :">

comment:7 Changed 9 years ago by limodou@…

I tried two pattern, both are right.

comment:8 Changed 9 years ago by mhf@…

This patch depends on rsplit, a function not found in python2.3:

>>> "foo.bar.baz".rsplit('.')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'str' object has no attribute 'rsplit'

The guilty line is in templatetags/init.py, line 5:

        __path__.extend(__import__(a.rsplit('.', 1)[0], '', '', ['']).__path__)

I think this could be used instead:

        __path__.extend(__import__('.'.join(a.split('.')[:a.count('.') and -1 or None])), '', '', ['']).__path__)

Changed 9 years ago by limodou

move rsplit to rindex and reduce duplicate path entry in path

Changed 9 years ago by limodou

change rsplit to rindex. (upload again, last patch disappeared)

comment:9 Changed 9 years ago by limodou

Why my attachment can not be accepted? I upload it successful, but I cann't see them.

comment:10 Changed 9 years ago by limodou

My god, after submit last comment, I see them.

comment:11 Changed 8 years ago by anonymous

  • Triage Stage changed from Unreviewed to Design decision needed

comment:12 Changed 8 years ago by Michael Radziej <mir@…>

  • Triage Stage changed from Design decision needed to Unreviewed

No anonymous stage changes, please.

comment:13 Changed 8 years ago by Simon G. <dev@…>

  • Has patch set
  • Needs tests set
  • Summary changed from [Patch]Custom tags and filters can be restricted by namespace to Custom tags and filters can be restricted by namespace
  • Triage Stage changed from Unreviewed to Design decision needed

comment:14 Changed 7 years ago by adrian

  • Triage Stage changed from Design decision needed to Accepted

Yes, yes, yes. This is a great suggestion.

comment:15 Changed 7 years ago by anonymous

  • Needs documentation set
  • Owner changed from nobody to anonymous
  • Patch needs improvement set
  • Status changed from new to assigned

patch no longer applies

comment:16 Changed 7 years ago by racter

  • Owner changed from anonymous to racter
  • Status changed from assigned to new

Changed 7 years ago by racter

adjusted limodou's patch to apply to trunk as of 6213

comment:17 Changed 7 years ago by anonymous

  • Patch needs improvement unset

comment:18 Changed 7 years ago by racter

  • Owner changed from racter to anonymous
  • Patch needs improvement set
  • Status changed from new to assigned

actually my revision of that patch still has some problems - i'm writing tests now & will try to work it out.

comment:19 Changed 7 years ago by durdinator

  • Cc me@… added

This will need to play nicely with [6289] -- loading templatetags from subdirectories.

Due to that change, a conflict between app_name.tag_library and tag_library.subdirectory could arise:

foo/
    templatetags/
        bar.py

baz/
    templatetags/
        foo/
            bar.py

In such a case, the app_name.tag_library should take priority.

comment:20 Changed 7 years ago by durdinator

  • Needs tests unset
  • Patch needs improvement unset

OK, this looks like it's stagnating, and it's something I definitely want. So here's a patch against [6507]. It's not based on limodou's or racter's work, and is more comprehensive in what it tidies up. Here's a summary of the result:

All tag libraries have an associated fully qualified name, which looks like "app_name.tag_library_name"; this fully qualified name can be used instead of the unqualified library_name in the {% load %} tag:

Normally we'd do
{% load humanize %}
But if another installed app has a 'humanize' tag libary, we might need to do
{% load django.contrib.humanize.humanize %}

If more than one installed app has a tag library with the same name, then the app closest to the start of the INSTALLED_APPS list will take precedence if the unqualified name is used.

Correspondingly, templatetags and filters are also namespaced -- by the name of the tag library as given to {% load %}.
So you can do this:

{% load library %}
{% load otherapp.library %}

{% library.mytag %} # Outputs "A"
{% otherapp.library.mytag %} # Outputs "B"

But not this:

{% load firstapp.library %}
{% library.mytag %} # We've not loaded anything as 'library'

If there is a conflict in tag names, then the last loaded tag library takes precedence (the same as the behaviour in trunk in such a case).

All that applies to tags also applies to filters, of course.

The standard django tag libraries can be accessed via their fully qualified names "django.defaulttags", "django.defaultfilters", "django.loader_tags", and "django.i18n".

Possible backward-incompatible changes (not sure if any of these changed interfaces were intended to be publically used):

  • The items of django.template.libraries and django.template.builtins are now tuples, not Library instances.
  • Template.add_library and django.template.add_to_builtins require additional arguments.
  • The django.templatetags package no longer augments its path with from all installed apps' "templatetags" packages -- instead it is limited to the standard set of django tags.

A definite (though doubtless rare) backward-incompatible change:

  • {% load foo.bar %} will load from foo<app>.templatetags.bar in preference to <some_app>.templatetags.foo.bar.

Final notes:

The django and contrib tests that used template.libraries and/or add_to_builtins have been updated, so they should still pass (I've not checked the humanize or markup tests though).

The admin docs has been updated to list all tag libraries available, and list the fully qualified name alongside the unqualified name.

And I still need to modify the documentation to accommodate these changes, but I'll do that in another patch, because it's getting late.

comment:21 Changed 7 years ago by durdinator

Updated patch against [6589]; the docs for the "load" tag have been updated.

Changed 7 years ago by durdinator

Patch against [6589], including tests and docs

comment:22 Changed 6 years ago by anonymous

  • Keywords djangocon added

comment:23 Changed 6 years ago by ericholscher

  • Needs tests set
  • Patch needs improvement set

I'm not assigning myself this ticket because I have a feeling that it will be evil and hard to do. I think that it can be made backwards compatible with an 'opt in' kind of model for it if it gets into core. In that the old functionality stays the default and if you want/need namespaces, then you can explicitly declare them. Then do the normal depreciation process from then out.

I am interested in helping with this however, so when some momentum gets going and I have some more time in a month or 2, Let's get this done.

comment:24 Changed 6 years ago by ericholscher

  • Owner anonymous deleted
  • Status changed from assigned to new

comment:25 Changed 6 years ago by simon

One way to do this in a backwards compatible manner would be to introduce a new {% import %} tag, which behaves like {% load %} except tags included using {% import %} must be accessed via a namespace. For example:

{% import humanize %}

{{ num|humanize.apnumber }}

Then we can encourage people to start using {% import %} and deprecate {% load %}, then remove load entirely later on.

comment:26 Changed 6 years ago by ramusus

  • Cc ramusus@… added

comment:27 Changed 5 years ago by emulbreh

See also: #12772 (duplicate)

comment:28 Changed 4 years ago by lrekucki

  • Type changed from enhancement to New feature

comment:29 Changed 4 years ago by lrekucki

  • Severity changed from normal to Normal

comment:30 Changed 3 years ago by anonymous

  • Easy pickings unset
  • UI/UX unset

This would be a fantastic addition. Coming from Grails, all of the taglib's there allow a namespace and it is extremely helpful in organization and maintenance of a project.

comment:31 Changed 3 years ago by mhf@…

  • Cc mhf@… added

comment:32 Changed 21 months ago by jonathanslenders

Not entirely sure, but I think that if #20434 gets merged, it should also be fairly easy to add namespacing as part of the templatetag grammar. (If that's still a way we'd like to go.)
For instance, for i18n.blocktrans, it becomes:

{% load i18n %}
{% i18n.blocktrans %}...{% i18n.endblocktrans %}
Last edited 21 months ago by jonathanslenders (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top