CRUD Viewset

CRUD or create, read, update, and delete are the four basic functions on Django Model. Viewflow provides ready-to-use CRUD viewsets, views, and templates based on google material design. Combined all together they allow to implement Django admin like functionality.

You can quickly instantiate a Viewset by passing required parameters to a class constructor

from viewflow import Icon
from viewflow.urls import ReadonlyModelViewset, ModelViewset

categories_viewset = ReadonlyModelViewset(

Or inherit from a viewset class and override methods and attributes

class DepartmentViewset(ModelViewset):
    icon = Icon('people')
    model = models.Department
    list_columns = ('name', 'manager', 'parent')
    list_filter_fields = ('parent', )

After inclusion into an Application viewset, you will get a model list page with links points to model details or edit pages.

ModelViewset is the viewset that mixes list model view with create/update views. Use DetailViewMixin to point links from list view to model detail page, before change form. DetailViewMixin adds ability to delete a model instance.

ReadonlyModelViewset only list a model and provide model details page.

Basic options

The only mandatory option for CRUD viewsets is the model class. You would also like to customize icon and title appearance in the site menu.

To optimize querying or restrict models listed, specify queryset attribute or override get_queryset method.

class EmployeeViewset(DetailViewMixin, ModelViewset):
    model = models.Employee
    queryset = model._default_manager.select_related('department')

    def get_queryset(self, request);
         if not request.user.is_staff:
              return self.queryset.exclude(department__parent_isnull=True)
         return self.queryset

You can replace a build-in view with our own functional or class-based view. Or pass additional keyword parameters to .as_view call

list_view_class = views.EmployeeListView
create_view = views.create_employee_view

def get_update_view_kwargs(self):
    return {
        'success_url': reverse('emp:employee:index')

As for any viewset, you can add additional views, just by adding an attribute named with _url suffix

manager_change_url =  path(
    '<path:pk>/manages/', views.change_manager, name='change_manager'

Form options

In Viewflow, you have the ability to customize form layouts and other aspects of your CRUD viewsets through various options.

Available Options:

  • create_form_layout: Sets the layout for the Create form.
  • create_form_class: Specifies the class to use for the Create form.
  • create_form_widgets: Defines the widgets to use in the Create form.
  • update_form_layout: Sets the layout for the Update form.
  • update_form_class: Specifies the class to use for the Update form.
  • update_form_widgets: Defines the widgets to use in the Update form.
  • form_layout: Sets the layout for both Create and Update forms if not individually specified.
  • form_class: Specifies the class for both Create and Update forms if not individually specified.
  • form_widgets: Defines the widgets for both Create and Update forms if not individually specified.
class ContinentViewset(ModelViewset):
    # ... other options ...

    create_form_layout = Layout(
        # Layout configuration for create form

    form_layout = Layout(
        # Layout configuration

    update_form_class = forms.ContinentForm

    form_widgets = {
        'planet': DependentModelSelect(
            queryset=lambda galaxy: Planet.objects.filter(galaxy=galaxy)

List options


Set list_columns to control which fields are displayed on the change list view.

If you don’t set list_columns, the list view will display a single column that with __str__() representation of each object.

There are three types of values that can be used in list_display:

The name of a model field. For example:

class EmployeeViewset(ModelViewset):
    list_columns = ('first_name', 'last_name')

A string representing a Viewset method that accepts one argument, the model instance. For example:

class EmployeeViewset(ModelViewset):
    list_columns = ('upper_case_name',)

    def upper_case_name(self, obj):
        return ("%s %s" % (obj.first_name, obj.last_name)).upper()
    upper_case_name.short_description = 'Name'

Or a string representing a model attribute or method (without any required arguments)



List filterset


To allow users to filter the list of displayed items Viewflow offers two key options for setting filters: list_filter_fields and list_filterset_class.

The list_filter_fields option allows you to specify a tuple of fields based on which the user can filter the list view. This is a simple but effective way to add basic filtering capabilities to your viewset.

class CityViewset(ExportViewsetMixin, DetailViewMixin, DeleteViewMixin, ModelViewset):
    list_filter_fields = ('is_capital', 'country', )

In the above example, the list view for cities can be filtered by whether the city is a capital and by its country.


For more advanced filtering needs, you can use the list_filterset_class option. This allows you to specify a custom django_filters.FilterSet class that defines the available filters and their behavior.

from django_filters import FilterSet, ModelChoiceFilter
from .models import Ocean, Sea

class SeaFilterSet(FilterSet):
    parent = ModelChoiceFilter(
    ocean = ModelChoiceFilter(queryset=Ocean.objects.all(), help_text='')

class SeaViewset(DeleteViewMixin, ModelViewset):
    list_filterset_class = filters.SeaFilterSet

In this example, the SeaFilterSet class defines two filters: one for the parent sea and one for the ocean. These filters are then applied to the SeaViewset through the list_filterset_class option.


Viewflow CRUd viewsets check standard Django add/change/delete/view user per-object permissions. Unlike default django behavior, if user have no-object specific permission, for example if user.has_perm(‘myapp.change_employee’, obj=None) equals True, default viewflow behavior is to assume that user have the permission for all objects. on if has_perm with obj=None return False object specific permission is checked. You can override in corresponding has_view_permission, has_add_permission, has_change_permission, has_delete_permission methods.

def has_delete_permission(self, request, obj=None):
    return request.user.is_staff


Pre-built views for admin like interfaces. All viewflow.views are inherited from core Django generic views with very few additions and method redefinitions.

All of them are accepts viewset as keyword parameter for the .as_view() method. If viewset present, the viewset methods and options would be used, permission checking methods like has_add_permission need to be overridden only in a viewset, and they would be used by a view. Same for get_queryset method.