ListView

The Fastview generic ListView adds support for admin-style filtering, searching and ordering, with a default template similar to the Django admin changelist. These can all be changed and customised as necessary.

Templates

The ListView follows Fastview’s standard template lookup rules

  • annotated objects

  • filters

  • search_fields

  • page_range

  • label_orders

Display fields

The fields attribute can take a list of model attribute names:

class BlogList(ListView):
    fields = ["name", "is_published"]

These are converted into display objects; to customise how a field is shown, or to add a custom field which isn’t a model attribute, you can create your own DisplayValue class and pass that in as an object in the list. See Display objects for full details.

Filters

The ListView supports filters = [...] attribute. This can be a list of model field names or Filter instances.

Fastview has 3 built-in filters:

  • Filter: a choice-based filter. Choices can either be specified on the constructor, on a subclass, or collected from the model field the filter is acting on. Subclasses can also define their own get_choices() method to override the default behaviour.

  • DateHierarchyFilter: lists active years for the DateField or DateTimeField, and then when a year is selected, filters by that year and lists active months to drill down to.

  • BooleanFilter: Yes/No filter for a BooleanField

If you specify the name of the field in the filters list, the most appropriate filter will be chosen for you.

Custom filter on a field

It can be useful to filter a field by options other than its values.

For example, to allow you to filter an empty CharField:

from fastview import ListView
from fastview.views.filters import Filter

class EmptyFilter(Filter):
    choices = (
        ('empty': 'Empty'),
        ('set': 'Set'),
    )

    def process(self, qs):
        if self.value == 'empty':
            return qs.filter(**{self.field_name: ''})
        elif self.value == 'set':
            return qs.exclude(**{self.field_name: ''})
        return qs

class Entries(ListView):
    model = Entry
    filters = ['publish_date', 'is_published', EmptyFilter('title')]

Custom filter on a method

It can also be useful to filter on values returned from model methods, such as the was_published_recently field in the tutorial. Because it’s a method not a field, it can’t be used to filter a queryset directly, but we can write a custom filter to do it instead:

polls/views.py
import datetime
from fastview.views.filters import BooleanFilter

class RecentlyPublishedFilter(BooleanFilter):
    def process(self, qs):
        if self.boolean is None:
            return qs

        now = timezone.now()
        date_range = {
            "pub_date__gte": now - datetime.timedelta(days=30),
            "pub_date__lte": now,
        }

        if self.boolean is True:
            return qs.filter(**date_range)
        return qs.exclude(**date_range)

class PollViewGroup(ModelViewGroup):
    # ...
    index_view = {
        # ...
        "filters": ["pub_date", RecentlyPublishedFilter(param="recent")],
    }

There are few things going on here:

  • Our filter subclasses fastview.views.filters.BooleanFilter, as that’s going to be the closest built-in filter to what we need - it lets the user pick yes or no.

  • We override the process method to filter the queryset. self.value is going to be True or False, or None if neither is selected.

  • We then instantiate our filter class with param="recent", which tells Fastview the key to use in the URL querystring, eg /polls/?recent=1 will make the filter value True.

  • The instantiated filter class can then be put in the filters array in the order you want the filters to be shown.

API reference

class fastview.views.generic.ListView(**kwargs)

Bases: fastview.views.mixins.DisplayFieldMixin, fastview.views.mixins.ModelFastViewMixin, django.views.generic.list.ListView

A permission-aware ListView with support for ViewGroups

action: Optional[str] = 'list'

Name of action for the ViewGroup

action_label: Optional[str] = 'List'

Label for this action

context_annotated_name = 'annotated_object_list'

Context variable name for the annotated object list.

default_template_name: str = 'fastview/list.html'

The default template name

fields: Optional[List[Union[str, DisplayValue]]] = [<fastview.views.display.ObjectValue object>]

Example:

fields = [
    'field_name',  # MyModel.field_name
    'get_value',  # MyModel.get_value()
    CustomValue(),  # :class:`fastview.views.display.DisplayValue` subclass

Defaults to show the string value of the model instance

filters: Optional[List[Union[str, BaseFilter]]] = None

List of available filters to apply to the list table.

The values can be filter classes or string values which correspond to model field names.

Example:

filters = [
    'field_name',  # MyModel.field_name
    CustomFilter(...), # :class:`fastview.views.filters.Filter` subclass
get_context_data(**kwargs)

The template context has additional variables available:

annotated_object_list: List of (object, Can, fields) tuples
filters: List of filters, bound to any query arguments
page_range: Paginator page range (Django 3.2+)
label_orders: List of (label, current_order, param_value) tuples for
    links in the table header
get_filters() Dict[str, fastview.views.filters.BaseFilter]

Build filter list by looking up field strings and converting to Filter instances

Convert self.filters from:

['field_name', Filter('param', ...)]

to:

{
    'field_name': Filter('field_name', ...),
    'param': Filter('param', ...),
}
get_ordering()

Build queryset ordering rule from the CSV list of DisplayValue slugs in request.GET[PARAM_ORDER]

get_queryset() django.db.models.query.QuerySet

Apply filters, search terms, ordering and limits to the queryset

object_annotator_factory(object_list)

Generate an annotated object list without holding everything in memory

Returns

A generator factory to annotate an object list.

In other words:

  • This takes a list of objects

  • It returns a clean generator function to iterate over those objects

  • It is not called until it reaches the template

  • It will be called each time it is referenced in the template, creating a clean generator, allowing the list to be iterated over more than once in the same template.

  • The generator yields AnnotatedObject objects for rendering

request_ordering: Optional[Dict[str, str]] = None

Ordering params found in the request when getting the queryset

row_permission = None

Permission check to display individual rows.

search_fields: Optional[List[str]] = None

List of fields to search

title: str = '{verbose_name_plural}'

The page title, passed to template context for use in page title and headers. See get_title() for more details