Opened 18 years ago
Last modified 11 years ago
#2539 new New feature
Custom tags and filters can be restricted by namespace
Reported by: | 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)
Change History (38)
by , 18 years ago
Attachment: | template.diff added |
---|
comment:1 by , 18 years ago
Summary: | Can custom tag use namespace → [Patch]Custom tags and filters can be restricted by namespace |
---|
comment:2 by , 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 , 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:5 by , 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:8 by , 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 , 18 years ago
Attachment: | template_02.diff added |
---|
move rsplit to rindex and reduce duplicate path entry in path
by , 18 years ago
Attachment: | template_03.diff added |
---|
change rsplit to rindex. (upload again, last patch disappeared)
comment:9 by , 18 years ago
Why my attachment can not be accepted? I upload it successful, but I cann't see them.
comment:11 by , 18 years ago
Triage Stage: | Unreviewed → Design decision needed |
---|
comment:12 by , 18 years ago
Triage Stage: | Design decision needed → Unreviewed |
---|
No anonymous stage changes, please.
comment:13 by , 18 years ago
Has patch: | set |
---|---|
Needs tests: | set |
Summary: | [Patch]Custom tags and filters can be restricted by namespace → Custom tags and filters can be restricted by namespace |
Triage Stage: | Unreviewed → Design decision needed |
comment:14 by , 17 years ago
Triage Stage: | Design decision needed → Accepted |
---|
Yes, yes, yes. This is a great suggestion.
comment:15 by , 17 years ago
Needs documentation: | set |
---|---|
Owner: | changed from | to
Patch needs improvement: | set |
Status: | new → assigned |
patch no longer applies
comment:16 by , 17 years ago
Owner: | changed from | to
---|---|
Status: | assigned → new |
by , 17 years ago
Attachment: | template_04.diff added |
---|
adjusted limodou's patch to apply to trunk as of 6213
comment:17 by , 17 years ago
Patch needs improvement: | unset |
---|
comment:18 by , 17 years ago
Owner: | changed from | to
---|---|
Patch needs improvement: | set |
Status: | new → assigned |
actually my revision of that patch still has some problems - i'm writing tests now & will try to work it out.
comment:19 by , 17 years ago
Cc: | 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 , 17 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 fromfoo<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 , 17 years ago
Updated patch against [6589]; the docs for the "load" tag have been updated.
by , 17 years ago
Attachment: | patch_2539_code_tests.diff added |
---|
Patch against [6589], including tests and docs
comment:22 by , 16 years ago
Keywords: | djangocon added |
---|
comment:23 by , 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 , 16 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:25 by , 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 , 15 years ago
Cc: | added |
---|
comment:28 by , 14 years ago
Type: | enhancement → New feature |
---|
comment:29 by , 14 years ago
Severity: | normal → Normal |
---|
comment:30 by , 13 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 , 13 years ago
Cc: | added |
---|
comment:32 by , 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 %}
This patch include three modified files. So you can load a custom taglib in two styles in template file:
Then you can use tags and filters through three styles, for example:
and you can also apply this style to filter, for example: