Skip to content

Instantly share code, notes, and snippets.

@chronossc
Created August 23, 2011 13:22
Show Gist options
  • Save chronossc/1165091 to your computer and use it in GitHub Desktop.
Save chronossc/1165091 to your computer and use it in GitHub Desktop.
A Django Tag that can do filter and exclude from a queryset, assigning to another context variable.
class FilterAndAssignNode(template.Node):
def __init__(self,parser=None,to=None,query=None,filter=None,exclude=None):
self.parser = parser
self.to = to
self.query = query
self.filter = filter or []
self.exclude = exclude or []
def render(self,context):
try:
queryobj = template.Variable(self.query).resolve(context)
assert isinstance(queryobj,QuerySet)
except template.VariableDoesNotExist:
raise template.VariableDoesNotExist,u"Can't get queryobj from context in tag filter_and_assign."
except AssertionError:
raise ValueError,u"Tag filter_and_assign expects a QuerySet object as queryobj."
filters = SortedDict()
excludes = SortedDict()
def parse_filters(filter):
# the lookup format is validated in compilation function, but not each lookup...
match = kwarg_re.match(filter)
lookup,value = match.groups()
try:
value = self.parser.compile_filter(value).resolve(context)
except template.VariableDoesNotExist:
pass
return lookup,value
for l in self.filter:
lookup, value = parse_filters(l)
filters[lookup] = value
for l in self.exclude:
lookup, value = parse_filters(l)
excludes[lookup] = value
if excludes:
queryobj = queryobj.excludes(**filters)
if filters:
queryobj = queryobj.filter(**filters)
context[self.to] = queryobj
@register.tag
def filter_and_assign(parser, token):
"""
Do a filter or a exclude in a queryset and assign result to a variable in
context. Values are treated as lookup dict and this tag will try to fint it
first on context.
Syntax::
{% filter_and_assign [to_name] [queryobj] filter=[filter_lookups] exclude=[exclude_lookups] %}
Example::
{% filter_and_assign counts historico_counts filter=unidade__modelo__pk=obj.id %}
"""
bits = token.contents.split()
if len(bits) < 4 or len(bits) > 5:
raise template.TemplateSyntaxError(u"'%s' tag takes between 3 and 4 arguments" % bits[0])
kwargs = {
'to': bits[1],
'query': bits[2],
'filter': None,
'exclude': None,
'parser': parser, # parser will be used in node ;)
}
for bit in bits[3:4]:
match = kwarg_re.match(bit)
if not match:
raise TemplateSyntaxError(u"Malformed arguments to filter_and_assign tag: %s" % bit)
name, lookups = match.groups()
if name not in kwargs.keys():
raise TemplateDoesNotExist(u"Tag filter_and_assign does not accept this method %s yet" % name)
lookups = lookups.split(',')
for lookup in lookups:
match = kwarg_re.match(lookup)
if not match:
raise TemplateSyntaxError(u"Malformed lookups to filter_and_assign tag: %s" % lookup)
kwargs[name] = lookups # lookups will be resolved in node.render()
return FilterAndAssignNode(**kwargs)
@kayprogrammer
Copy link

Can this be used to exclude a certain item in a for loop in the Django templates?

@chronossc
Copy link
Author

Can this be used to exclude a certain item in a for loop in the Django templates?

@Hz-Kene this is quite old now, 10 years already. It uses Django QuerySet directly, and so you will have a new QuerySet at end that can be consumed by a for-loop. I would use python filter before send the iterable to Django templates in your case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment