Opened 18 years ago

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

Download all attachments as: .zip

Change History (38)

by limodou, 18 years ago

Attachment: template.diff added

comment:1 by limodou@…, 18 years ago

Summary: Can custom tag use namespace[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 by limodou@…, 18 years ago

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 by anonymous, 18 years ago

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

comment:4 by limodou, 18 years ago

Right. I'll change it.

by limodou@…, 18 years ago

Attachment: template_01.diff added

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

comment:5 by Tom Tobin <korpios@…>, 18 years ago

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 by Tom Tobin <korpios@…>, 18 years ago

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

comment:7 by limodou@…, 18 years ago

I tried two pattern, both are right.

comment:8 by mhf@…, 18 years ago

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__)

by limodou, 18 years ago

Attachment: template_02.diff added

move rsplit to rindex and reduce duplicate path entry in path

by limodou, 18 years ago

Attachment: template_03.diff added

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

comment:9 by limodou, 18 years ago

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

comment:10 by limodou, 18 years ago

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

comment:11 by anonymous, 17 years ago

Triage Stage: UnreviewedDesign decision needed

comment:12 by Michael Radziej <mir@…>, 17 years ago

Triage Stage: Design decision neededUnreviewed

No anonymous stage changes, please.

comment:13 by Simon G. <dev@…>, 17 years ago

Has patch: set
Needs tests: set
Summary: [Patch]Custom tags and filters can be restricted by namespaceCustom tags and filters can be restricted by namespace
Triage Stage: UnreviewedDesign decision needed

comment:14 by Adrian Holovaty, 17 years ago

Triage Stage: Design decision neededAccepted

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

comment:15 by anonymous, 17 years ago

Needs documentation: set
Owner: changed from nobody to anonymous
Patch needs improvement: set
Status: newassigned

patch no longer applies

comment:16 by racter, 17 years ago

Owner: changed from anonymous to racter
Status: assignednew

by racter, 17 years ago

Attachment: template_04.diff added

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

comment:17 by anonymous, 17 years ago

Patch needs improvement: unset

comment:18 by racter, 17 years ago

Owner: changed from racter to anonymous
Patch needs improvement: set
Status: newassigned

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

comment:19 by durdinator, 17 years ago

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 by durdinator, 16 years ago

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 by durdinator, 16 years ago

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

by durdinator, 16 years ago

Attachment: patch_2539_code_tests.diff added

Patch against [6589], including tests and docs

comment:22 by anonymous, 16 years ago

Keywords: djangocon added

comment:23 by Eric Holscher, 16 years ago

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 by Eric Holscher, 16 years ago

Owner: anonymous removed
Status: assignednew

comment:25 by simon, 16 years ago

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 by ramusus, 15 years ago

Cc: ramusus@… added

comment:27 by Johannes Dollinger, 14 years ago

See also: #12772 (duplicate)

comment:28 by Łukasz Rekucki, 13 years ago

Type: enhancementNew feature

comment:29 by Łukasz Rekucki, 13 years ago

Severity: normalNormal

comment:30 by anonymous, 12 years ago

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 by mhf@…, 12 years ago

Cc: mhf@… added

comment:32 by jonathanslenders, 11 years ago

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 11 years ago by jonathanslenders (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top