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(
app_name='category',
icon=Icon('category'),
model=models.Category,
list_view=views.custom_list_view,
)
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.
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'
)
In Viewflow, you have the ability to customize form layouts and other aspects of your CRUD viewsets through various 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(
depends_on='galaxy',
queryset=lambda galaxy: Planet.objects.filter(galaxy=galaxy)
)
}
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)
Use list_object_link_columns to control if and which fields in list_display should be linked to the “change” or “detail” page for an object.
TODO
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(
queryset=Sea.objects.filter(
pk__in=Sea.objects.filter(parent__isnull=False).values('parent')
)
)
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.