142
You can’t do this, which is by design. The Django framework authors intended a strict separation of presentation code from data logic. Filtering models is data logic, and outputting HTML is presentation logic.
So you have several options. The easiest is to do the filtering, then pass the result to render_to_response
. Or you could write a method in your model so that you can say {% for object in data.filtered_set %}
. Finally, you could write your own template tag, although in this specific case I would advise against that.
54
I just add an extra template tag like this:
@register.filter
def in_category(things, category):
return things.filter(category=category)
Then I can do:
{% for category in categories %}
{% for thing in things|in_category:category %}
{{ thing }}
{% endfor %}
{% endfor %}
- [Django]-Retrieving parameters from a URL
- [Django]-Django Rest Framework with ChoiceField
- [Django]-Django signals vs. overriding save method
15
I run into this problem on a regular basis and often use the "add a method" solution. However, there are definitely cases where "add a method" or "compute it in the view" don’t work (or don’t work well). E.g. when you are caching template fragments and need some non-trivial DB computation to produce it. You don’t want to do the DB work unless you need to, but you won’t know if you need to until you are deep in the template logic.
Some other possible solutions:
-
Use the {% expr <expression> as <var_name> %} template tag found at http://www.djangosnippets.org/snippets/9/ The expression is any legal Python expression with your template’s Context as your local scope.
-
Change your template processor. Jinja2 (https://jinja.palletsprojects.com/) has syntax that is almost identical to the Django template language, but with full Python power available. It’s also faster. You can do this wholesale, or you might limit its use to templates that you are working on, but use Django’s "safer" templates for designer-maintained pages.
- [Django]-Django character set with MySQL weirdness
- [Django]-Django admin and MongoDB, possible at all?
- [Django]-Getting the SQL from a Django QuerySet
12
The other option is that if you have a filter that you always want applied, to add a custom manager on the model in question which always applies the filter to the results returned.
A good example of this is a Event
model, where for 90% of the queries you do on the model you are going to want something like Event.objects.filter(date__gte=now)
, i.e. you’re normally interested in Events
that are upcoming. This would look like:
class EventManager(models.Manager):
def get_query_set(self):
now = datetime.now()
return super(EventManager,self).get_query_set().filter(date__gte=now)
And in the model:
class Event(models.Model):
...
objects = EventManager()
But again, this applies the same filter against all default queries done on the Event
model and so isn’t as flexible some of the techniques described above.
- [Django]-How to mix queryset results?
- [Django]-Unittest Django: Mock external API, what is proper way?
- [Django]-What is actually assertEquals in Python?
12
This can be solved with an assignment tag:
from django import template
register = template.Library()
@register.assignment_tag
def query(qs, **kwargs):
""" template tag which allows queryset filtering. Usage:
{% query books author=author as mybooks %}
{% for book in mybooks %}
...
{% endfor %}
"""
return qs.filter(**kwargs)
EDIT: assignment_tag was removed in Django 2.0, this will no longer work.
- [Django]-NumPy array is not JSON serializable
- [Django]-How to resize an ImageField image before saving it in python Django model
- [Django]-How to check if ManyToMany field is not empty?
2
This is my approach:
@register.filter()
def query_filter(value, attr):
return value.filter(**eval(attr))
In the template:
{{ queryset|query_filter:'{"cod_tipoinmueble":1,"des_operacion": "alquiler"}'|length }}
- [Django]-Specifying limit and offset in Django QuerySet wont work
- [Django]-Django "xxxxxx Object" display customization in admin action sidebar
- [Django]-Django set field value after a form is initialized
1
For anyone looking for an answer in 2020.
This worked for me.
In Views:
class InstancesView(generic.ListView):
model = AlarmInstance
context_object_name = 'settings_context'
queryset = Group.objects.all()
template_name = 'insta_list.html'
@register.filter
def filter_unknown(self, aVal):
result = aVal.filter(is_known=False)
return result
@register.filter
def filter_known(self, aVal):
result = aVal.filter(is_known=True)
return result
In template:
{% for instance in alarm.qar_alarm_instances|filter_unknown:alarm.qar_alarm_instances %}
In pseudocode:
For each in model.child_object|view_filter:filter_arg
Hope that helps.
- [Django]-Proper way to handle multiple forms on one page in Django
- [Django]-How to access array elements in a Django template?
- [Django]-What are the differences between django-tastypie and djangorestframework?
1
you can use custom template filter
{% for object in data.somekey_set.FILTER %}
to
{% for object in data.somekey_set|filter_name %}
create filter.py
@register.filter(name='filter_name')
def filter_name(value):
return value.filter(your rule)
- [Django]-How to set up custom middleware in Django?
- [Django]-List_display – boolean icons for methods
- [Django]-Where's my JSON data in my incoming Django request?