Changelist Forms

Changelist Forms are designed to facilitate easy searching and sorting on django models, along with providing a framework for building other functionality that deals with operations on lists of model instances.

class betterforms.changelist.ChangeListForm

Base form class for all Changelist forms.

  • setting the queryset:

    All changelist forms need to know what queryset to begin with. You can do this by either passing a named keyword parameter into the contructor of the form, or by defining a model attribute on the class.

class betterforms.changelist.SearchForm[source]
Form class which facilitates searching across a set of specified fields for a model. This form adds a field to the model q for the search query.

The list of fields that will be searched against.

Whether the search should be case sensitive.

Here is a simple SearchForm example for searching across users.

# my_app/
from django.contrib.auth.models import get_user_model
from betterforms.forms import SearchForm

class UserSearchForm(SearchForm):
    SEARCH_FIELDS = ('username', 'email', 'name')
    model = get_user_model()

 from my_app.forms import UserSearchForm

 def user_list_view(request):
     form = UserSearchForm(request.GET)
     context = {'form': form}
     if form.is_valid:
         context['queryset'] = form.get_queryset()
     return render_to_response(context, ...)

SearchForm checks to see if the query value is present in any of the fields declared in SEARCH_FIELDS by or-ing together Q objects using __contains or __icontains queries on those fields.

class betterforms.changelist.SortForm[source]
Form which facilitates the sorting instances of a model. This form adds a hidden field sorts to the model which is used to dictate the columns which should be sorted on and in what order.

The list of Header objects for sorting.

Headers can be declared in multiple ways.

  • As instantiated Header objects.:

    # my_app/
    from betterforms.forms import SortForm, Header
    class UserSortForm(SortForm):
        HEADERS = (
            Header('username', ..),
            Header('email', ..),
            Header('name', ..),
        model = User
  • As a string:

    # my_app/
    from betterforms.forms import SortForm
    class UserSortForm(SortForm):
        HEADERS = (
        model = User
  • As an iterable of *args which will be used to instantiate the Header object.:

    # my_app/
    from betterforms.forms import SortForm
    class UserSortForm(SortForm):
        HEADERS = (
            ('username', ..),
            ('email', ..),
            ('name', ..),
        model = User
  • As a two-tuple of header name and **kwargs. The name and provided **kwargs will be used to instantiate the Header objects.

    # my_app/
    from betterforms.forms import SortForm
    class UserSortForm(SortForm):
        HEADERS = (
            ('username', {..}),
            ('email', {..}),
            ('name', {..}),
        model = User

All of these examples are roughly equivilent, resulting in the form haveing three sortable headers, ('username', 'email', 'name'), which will map to those named fields on the User model.

See documentation on the Header class for more information on how sort headers can be configured.


Returns a list of column names that are used in the order_by call on the returned queryset.

During instantiation, all declared headers on form.HEADERS are converted to Header objects and are accessible from form.headers.

>>> [header for header in form.headers]  # Iterate over all headers.
>>> form.headers[2]  #  Get the header at index-2
>>> form.headers['username']  #  Get the header named 'username'
class betterforms.changelist.Header(name, label=None, column_name=None, is_sortable=True)[source]

Headers are the the mechanism through which SortForm shines. They provide querystrings for operations related to sorting by whatever query parameter that header is tied to, as well as other values that are helpful during rendering.


The name of the header.


The human readable name of the header.


Boolean as to whether this header is currently being used to sort.


Boolean as to whether this header is being used to sort the data in ascending order.


Boolean as to whether this header is being used to sort the data in descending order.


Space separated list of css classes, suitable for output in the template as an HTML element’s css class attribute.


1-indexed number as to the priority this header is at in the list of sorts. Returns None if the header is not active.


The querystring that will sort by this header as the primary sort, moving all other active sorts in their current order after this one. Preserves all other query parameters.


The querystring that will remove this header from the sorts. Preserves all other query parameters.


The querystring that will sort by this header, and deactivate all other active sorts. Preserves all other query parameters.

Working With Changelists

Outputting sort form headers can be done using a provided template partial located at betterforms/sort_form_header.html

<th class="{{ header.css_classes }}">
  {% if header.is_sortable %}
    <a href="?{{ header.querystring }}">{{ header.label }}</a>
    {% if header.is_active %}
      {% if header.is_ascending %}
      {% elif header.is_descending %}
      {% endif %}
      <a href="" data-sort_by="title" data-direction="up"></a>
      <span class="filterActive"><span>{{ header.priority }}</span> <a href="?{{ header.remove_querystring }}">x</a></span>
    {% endif %}
  {% else %}
    {{ header.label }}
  {% endif %}

This example assumes that you are using a table to output your data. It should be trivial to modify this to suite your needs.

class betterforms.views.BrowseView[source]

Class-based view for working with changelists. It is a combination of FormView and ListView in that it handles form submissions and providing a optionally paginated queryset for rendering in the template.

Works similarly to the standard FormView class provided by django, except that the form is instantiated using request.GET, and the object_list passed into the template context comes from form.get_queryset().