Jak przeprowadzić filtrowanie zapytań w szablonach django


83

Muszę wykonać filtrowane zapytanie z poziomu szablonu django, aby uzyskać zestaw obiektów odpowiadający kodowi Pythona w widoku:

queryset = Modelclass.objects.filter(somekey=foo)

W moim szablonie chciałbym to zrobić

{% for object in data.somekey_set.FILTER %}

ale po prostu nie mogę się dowiedzieć, jak napisać FILTR.

Odpowiedzi:


121

Nie możesz tego zrobić, co jest zgodne z projektem. Autorzy frameworka Django zamierzali ściśle oddzielić kod prezentacji od logiki danych. Filtrowanie modeli to logika danych, a generowanie kodu HTML to logika prezentacji.

Masz więc kilka opcji. Najłatwiej jest przeprowadzić filtrowanie, a następnie przekazać wynik do render_to_response. Możesz też napisać metodę w swoim modelu, aby móc powiedzieć {% for object in data.filtered_set %}. Na koniec możesz napisać własny tag szablonu, chociaż w tym konkretnym przypadku odradzałbym to.


2
Hello People to teraz rok 2014! Około 6 lat później biblioteki JS poczyniły ogromny postęp i filtrowanie niezbyt dużych ilości danych powinno być raczej wykonywane po stronie klienta przy wsparciu jakiejś fajnej biblioteki skryptów Java lub przynajmniej AJAX-ed.
andilabs

1
@andi: Z pewnością zgadzam się nawet na umiarkowanie duże zbiory danych, np. nawet tysiące wierszy w tabeli. Pracując nad bazami danych z milionami wierszy, wciąż jest miejsce na filtrowanie po stronie serwera :)
Eli Courtwright,

jasne, ale chciałem tylko wskazać na ludzi, którzy mają do czynienia z kilkoma tysiącami wierszy, że przyjemne doświadczenie interakcji dla użytkownika może się zdarzyć w przeglądarce. A dla ludzi, którzy mają nawet do czynienia z ogromnymi zbiorami danych, dobrym rozwiązaniem może być podejście hybrydowe, np. Filtrowanie w kategoriach od kilku M do kilku K po stronie serwera, a inni lżejsi pracownicy wewnątrz tych kilku K po stronie klienta.
andilabs

9
@andi Z wyjątkiem sytuacji, w których filtrujesz zawartość na podstawie uprawnień, które nigdy nie zostałyby wykonane po stronie klienta. Dobrze?

39

Po prostu dodaję dodatkowy tag szablonu, taki jak ten:

@register.filter
def in_category(things, category):
    return things.filter(category=category)

Wtedy mogę:

{% for category in categories %}
  {% for thing in things|in_category:category %}
    {{ thing }}
  {% endfor %}
{% endfor %}

Próbuję to rozwiązanie, ale to wciąż wyzwalania błąd: 'for' statements should use the format 'for x in y': for p in r | people_in_roll_department:d. Jakieś pomysły?
Diosney

@diosney prawdopodobnie dodasz „.all” w zdaniu rzeczy. Powinno być „things.all”
Enric Mieza

12

Regularnie napotykam ten problem i często korzystam z rozwiązania „dodaj metodę”. Jednak na pewno są przypadki, w których „dodaj metodę” lub „oblicz ją w widoku” nie działa (lub nie działa dobrze). Np. Kiedy buforujesz fragmenty szablonu i potrzebujesz trochę nietrywialnych obliczeń bazy danych, aby je stworzyć. Nie chcesz wykonywać pracy z bazą danych, chyba że musisz, ale nie będziesz wiedział, czy musisz, dopóki nie zagłębisz się w logikę szablonu.

Inne możliwe rozwiązania:

  1. Użyj tagu szablonu {% expr <expression> as <var_name>%} znajdującego się pod adresem http://www.djangosnippets.org/snippets/9/ Wyrażenie to dowolne zgodne z prawem wyrażenie Pythona z kontekstem szablonu jako zakresem lokalnym.

  2. Zmień procesor szablonów. Jinja2 ( http://jinja.pocoo.org/2/ ) ma składnię prawie identyczną z językiem szablonów Django, ale z pełną mocą Pythona. Jest też szybszy. Możesz to zrobić hurtowo lub ograniczyć jego użycie do szablonów, nad którymi pracujesz, ale użyj „bezpieczniejszych” szablonów Django dla stron obsługiwanych przez projektanta.


9

Inną opcją jest to, że jeśli masz filtr, który zawsze chcesz zastosować, możesz dodać niestandardowego menedżera do danego modelu, który zawsze stosuje filtr do zwracanych wyników.

Dobrym przykładem jest Eventmodel, w którym dla 90% zapytań, które wykonujesz na modelu, będziesz chciał czegoś takiego Event.objects.filter(date__gte=now), tj. Normalnie jesteś zainteresowany Eventstym, co nadchodzi. To wyglądałoby tak:

class EventManager(models.Manager):
    def get_query_set(self):
        now = datetime.now()
        return super(EventManager,self).get_query_set().filter(date__gte=now)

A w modelu:

class Event(models.Model):
    ...
    objects = EventManager()

Ale znowu, to stosuje ten sam filtr do wszystkich domyślnych zapytań wykonywanych w Eventmodelu, więc nie jest tak elastyczny w przypadku niektórych technik opisanych powyżej.


9

Można to rozwiązać za pomocą tagu przypisania:

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)

4
Przypisanie_tag zostało usunięte w Django 2.0
Andreas Bergström

1

Dla każdego, kto szuka odpowiedzi w 2020 roku. U mnie to zadziałało.

W widokach:

 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

W szablonie:

{% for instance in alarm.qar_alarm_instances|filter_unknown:alarm.qar_alarm_instances %}

W pseudokodzie:

For each in model.child_object|view_filter:filter_arg

Mam nadzieję, że to pomoże.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.