Package: django-tag-parser
django-tag-parserA micro-library to easily write custom Django template tags.Features:Functions to parse tags, especially: “args”, “kwargs”, and “as varname” syntax.Real OOP classes to write custom inclusion tags.Functions:parse_token_kwargs: split a token into the tag name, args and kwargs.parse_as_var: extract the “as varname” from a token.Base classes (intag_parser.basetags):BaseNode: A templateNodeobject which features some basic parsing abilities.BaseInclusionNode: aNodethat hasinclusion_taglike behaviour, but allows to override thetemplate_namedynamically.BaseAssignmentNode: aNodethat returns the value in the context, using theas varsyntax.BaseAssignmentOrOutputNode: aNodethat either displays the value, or inserts it in the context.BaseAssignmentOrInclusionNode: a class that allows a{% get_itemstemplate="..."%}and{% get_items as var %}syntax.The base classes allows to implement@register.simple_tag,@register.inclusion_tagand@register.assignment_taglike functionalities,
while still leaving room to extend the parsing, rendering or syntax validation.
For example, not all arguments need to be seen as template variables, filters or literal keywords.As of v3.0, the@template_tagdecorator is no longer needed.
Use@register.tag("name")directly on the class names.InstallationFirst install the module, preferably in a virtual environment. It can be installed from PyPI:pipinstalldjango-tag-parserExamplesAt the top of your template tags library, always include the standard
Djangoregistervariable and ourtemplate_tagdecorator:fromdjango.templateimportLibraryfromtag_parserimporttemplate_tagregister=Library()Arguments and keyword argumentsTo parse a syntax like:{%my_tag"arg1"keyword1="bar"keyword2="foo"%}use:fromdjango.templateimportLibraryfromtag_parser.basetagsimportBaseNoderegister=Library()@register.tag('my_tag')classMyTagNode(BaseNode):max_args=1allowed_kwargs=('keyword1','keyword2',)defrender_tag(self,context,*tag_args,**tag_kwargs):return"Tag Output"Inclusion tagsTo create an inclusion tag with overwritable template_name:{%my_include_tag"foo"template="custom/example.html"%}use:fromdjango.templateimportLibraryfromtag_parser.basetagsimportBaseInclusionNoderegister=Library()@register.tag("my_include_tag")classMyIncludeTag(BaseInclusionNode):template_name="mytags/default.html"max_args=1defget_context_data(self,parent_context,*tag_args,**tag_kwargs):(foo,)=*tag_argsreturn{'foo':foo}Theget_template_name()method can be overwritten too to support dynamic resolving of template names.
By default it checks thetemplatetag_kwarg, andtemplate_nameattribute.
Note the template nodes are cached afterwards, it’s not possible to return random templates at each call.Assignment tagsTo create assignment tags that can either render itself, or return context data:{%get_tagstemplate="custom/example.html"%}{%get_tagsaspopular_tags%}use:fromdjango.templateimportLibraryfromtag_parser.basetagsimportBaseAssignmentOrInclusionNoderegister=Library()@register.tag('get_tags')classGetPopularTagsNode(BaseAssignmentOrInclusionNode):template_name="myblog/templatetags/popular_tags.html"context_value_name='tags'allowed_kwargs=('order','orderby','limit',)defget_value(self,context,*tag_args,**tag_kwargs):returnquery_tags(**tag_kwargs)# Something that reads the tags.Block tagsTo have a “begin .. end” block, defineend_tag_namein the class:{%my_tagkeyword1=foo%}Tag contents, possibly other tags.{%end_my_tag%}use:fromdjango.templateimportLibraryfromtag_parser.basetagsimportBaseAssignmentOrInclusionNoderegister=Library()@register.tag('my_tag')classMyTagNode(BaseNode):max_args=1allowed_kwargs=('keyword1','keyword2',)end_tag_name='end_my_tag'defrender_tag(self,context,*tag_args,**tag_kwargs):# Render contents insidereturnself.nodelist.render(context)Custom parsingWith the standardNodeclass from Django, it’s easier to implement custom syntax.
For example, to parse:{%getfirstofval1val2asval3%}use:fromdjango.templateimportLibrary,Node,TemplateSyntaxErrorfromtag_parserimportparse_token_kwargs,parse_as_varregister=Library()@register.tag('getfirstof')classGetFirstOfNode(Node):def__init__(self,options,as_var):self.options=options# list of FilterExpression nodes.self.as_var=as_var@classmethoddefparse(cls,parser,token):bits,as_var=parse_as_var(parser,token)tag_name,options,_=parse_token_kwargs(parser,bits,allowed_kwargs=())ifas_varisNoneornotchoices:raiseTemplateSyntaxError("Expected syntax: {{%{0}val1 val2 as val %}}".format(tag_name))returncls(options,as_var)defrender(self,context):value=Noneforfilterexprinself.options:# The ignore_failures argument prevents that the value becomes TEMPLATE_STRING_IF_INVALID.value=filterexpr.resolve(context,ignore_failures=True)ifvalueisnotNone:breakcontext[self.as_var]=valuereturn''ContributingThis module is designed to be generic. In case there is anything you didn’t like about it,
or think it’s not flexible enough, please let us know. We’d love to improve it!If you have any other valuable contribution, suggestion or idea,
please let us know as well because we will look into it.
Pull requests are welcome too. :-)