How to do it...

To complete the recipe, follow these steps:

  1. We create MovieFilterForm with all of the possible categories to filter by:
# movies/forms.py
from django import forms
from django.utils.translation import ugettext_lazy as _

from .models import Genre, Director, Actor, RATING_CHOICES

class MovieFilterForm(forms.Form):
genre = forms.ModelChoiceField(
label=_("Genre"),
required=False,
queryset=Genre.objects.all())
director = forms.ModelChoiceField(
label=_("Director"),
required=False,
queryset=Director.objects.all())
actor = forms.ModelChoiceField(
label=_("Actor"),
required=False,
queryset=Actor.objects.all())
rating = forms.ChoiceField(
label=_("Rating"),
required=False,
choices=RATING_CHOICES)
  1. We create a movie_list view that will use MovieFilterForm to validate the request query parameters and perform the filtering for chosen categories. Note the facets dictionary that is used here to list the categories and also the currently selected choices:
# movies/views.py
from django.conf import settings
from django.shortcuts import render

from .models import Genre, Director, Actor, Movie, RATING_CHOICES
from .forms import MovieFilterForm


def movie_list(request):
qs = Movie.objects.order_by("title")
form = MovieFilterForm(data=request.GET)

facets = {
"selected": {},
"categories": {
"genres": Genre.objects.all(),
"directors": Director.objects.all(),
"actors": Actor.objects.all(),
"ratings": RATING_CHOICES,
},
}

if form.is_valid():
filters = (
("genre", "genres",),
("director", "directors",),
("actor", "actors",),
("rating", "rating",),
)
qs = filter_facets(facets, qs, form, filters)

if settings.DEBUG:
# Let's log the facets for review when debugging
import logging
logger = logging.getLogger(__name__)
logger.info(facets)

context = {
"form": form,
"facets": facets,
"object_list": qs,
}
return render(request, "movies/movie_list.html", context)

def filter_facets(facets, qs, form, filters):
for facet, key in filters:
value = form.cleaned_data[facet]
if value:
selected_value = value
if facet == "rating":
rating = int(value)
selected_value = (rating,
dict(RATING_CHOICES)[rating])
filter_args = {
f"{key}__gte": rating,
f"{key}__lt": rating + 1,
}
else:
filter_args = {key: value}
facets["selected"][facet] = selected_value
qs = qs.filter(**filter_args).distinct()
return qs
  1. If you haven't done so already, create a base.html template. You can do that according to the example provided in the Arranging the base.html template recipe in Chapter 4, Templates and JavaScript.
  2. For our movie list, we'll need a slight variation with a two-column layout, as follows:
{# base_two_columns.html #}
{% extends "base.html" %}

{% block container %}
<div class="container">
<div class="row">
<div id="sidebar" class="col-md-4">
{% block sidebar %}{% endblock %}
</div>
<div id="content" class="col-md-8">
{% block content %}{% endblock %}
</div>
</div>
</div>
{% endblock %}
  1. Each of the categories will follow a common pattern in the filters sidebar, so we can extract some common parts as include templates. First, we have the filter heading, corresponding to movies/includes/filter_heading.html, as in the following:
{# movies/includes/filter_heading.html #}
{% load i18n %}
<div class="panel-heading">
<h6 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion"
href="#collapse-{{ title|slugify }}s">{% blocktrans %}
Filter by {{ title }}{% endblocktrans %}</a>
</h6>
</div>
  1. And then each filter will contain a link to reset filtering for that category, represented by movies/includes/filter_all.html here. This uses the {% modify_query %} template tag, described in the Creating a template tag to modify request query parameters in Chapter 5Custom Template Filters and Tags, to generate URLs for the filters:
{# movies/includes/filter_all.html #}
{% load i18n utility_tags %}
<a class="list-group-item {% if not selected %}active{% endif %}"
href="{% modify_query "page" param %}">
{% trans "All" %}</a>
  1. We create the movies/movie_list.html template for the list view itself, which will use the facets dictionary to list the categories and know which category is currently selected:
{# movies/movie_list.html #}
{% extends "base_two_columns.html" %}
{% load utility_tags %}

{% block sidebar %}
<div class="filters panel-group" id="accordion">
{% with title="Genre" selected=facets.selected.genre %}
<div class="panel panel-default">
{% include "movies/includes/filter_heading.html"
with title=title %}
<div id="collapse-{{ title|slugify }}"
class="panel-collapse collapse in">
<div class="panel-body"><div class="list-group">
{% include "movies/includes/filter_all.html"
with param="genre" %}
{% for cat in facets.categories.genres %}
<a class="list-group-item
{% if selected == cat %}
active{% endif %}"
href="{% modify_query "page"
genre=cat.pk %}">
{{ cat }}</a>
{% endfor %}
</div></div>
</div>
</div>
{% endwith %}
{% with title="Director"
selected=facets.selected.director %}
<div class="panel panel-default">
{% include "movies/includes/filter_heading.html"
with title=title %}
<div id="collapse-{{ title|slugify }}"
class="panel-collapse collapse in">
<div class="panel-body"><div class="list-group">
{% include "movies/includes/filter_all.html"
with param="director" %}
{% for cat in facets.categories.directors %}
<a class="list-group-item
{% if selected == cat %}
active{% endif %}"
href="{% modify_query "page"
director=cat.pk %}">
{{ cat }}</a>
{% endfor %}
</div></div>
</div>
</div>
{% endwith %}
{% with title="Actor" selected=facets.selected.actor %}
<div class="panel panel-default">
{% include "movies/includes/filter_heading.html"
with title=title %}
<div id="collapse-{{ title|slugify }}"
class="panel-collapse collapse in">
<div class="panel-body"><div class="list-group">
{% include "movies/includes/filter_all.html"
with param="actor" %}
{% for cat in facets.categories.actors %}
<a class="list-group-item
{% if selected == cat %}
active{% endif %}"
href="{% modify_query "page"
actor=cat.pk %}">
{{ cat }}</a>
{% endfor %}
</div></div>
</div>
</div>
{% endwith %}
{% with title="Rating" selected=facets.selected.rating %}
<div class="panel panel-default">
{% include "movies/includes/filter_heading.html"
with title=title %}
<div id="collapse-{{ title|slugify }}"
class="panel-collapse collapse">
<div class="panel-body"><div class="list-group">
{% include "movies/includes/filter_all.html"
with param="rating" %}
{% for r_val, r_display
in facets.categories.ratings %}
<a class="list-group-item
{% if selected.0 == r_val %}
active{% endif %}"
href="{% modify_query "page"
rating=r_val %}">
{{ r_display }}</a>
{% endfor %}
</div></div>
</div>
</div>
{% endwith %}
</div>
{% endblock %}

{% block content %}
<div class="movie_list">
{% for movie in object_list %}
<div class="movie alert alert-info">
<p>{{ movie.title }}</p>
</div>
{% endfor %}
</div>
{% endblock %}
NOTE: Template tags in the previous snippet have been split across lines for legibility but, in practice, template tags must be on a single line, and so cannot be split in this manner.
  1. The movie list needs to be added to the URLs for the movies app:
# movies/urls.py
from django.urls import path

from .views import movie_list

urlpatterns = [
path('', movie_list, name='movie-list'),
]
  1. The movies app URLs need to be added to the project:
# project/urls.py
from django.urls import include, path

urlpatterns = [
# ...
path('movies/', include('movies.urls'),
]
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.233.205