Backend Model Options¶
Often a custom backend view is generated for a model automatically. This is why most aspects of how a backend view is presenting model instances are actually controlled via the model.
The idea is that the backend system is very generative. It provides common edit functionality based on the model that can then be customised on a case-by-case basis through backend view options.
Model View Options¶
Model view options are options declared by the model that alter the presentation and functionality of the corresponding model view that operates on the model as part of the backend system.
Let’s say that the following model view is installed as part of the backend
system to manage Books
:
from cubane.backend.views import BackendSection
from cubane.views import ModelView
from models import Book
class BookView(ModelView):
template_path = 'cubane/backend/'
model = Book
One of the first thing that you would want to customise is the columns that are presented by the model view. By default, a model view will simply extract a number of columns from the model automatically, which might not necessarily be the ones you wanted.
In order to customise the columns that are presented by the backend, simply
declare the class Listing
within your model class and declare a list of
columns:
from django.db import models
from cubane.models import DateTimeBase
class Book(DateTimeBase):
class Meta:
verbose_name = 'Book'
verbose_name_plural = 'Books'
ordering = ['title']
class Listing:
columns = [
'title',
'slug',
'isbn',
'recommended',
'price'
]
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, db_index=True)
isbn = models.CharField(max_length=32, db_index=True, unique=True)
published = models.DateField()
publisher = models.CharField(max_length=255)
author = models.CharField(max_length=255)
recommended = models.BooleanField(default=False)
price = models.DecimalField(max_digits=12, decimal_places=2)
@classmethod
def get_form(cls):
from forms import BookForm
return BookForm
def __unicode__(self):
return self.title
In the following, we will discuss various other Listing
options that may
affect the presentation and functionality of a model view:
columns
: List of visible columns.edit_columns
: List of editable columns.filter_by
: List of columns that can be filtered by.sortable
: True, if the model has a sequence order which can be changed.searchable
: List of additional columns that take part in quick searching.edit_view
: True, if bulk editing mode is available.grid_view
: True, if image-based grid view is available.default_view
: Default view type (list
,edit
orgrid
).default_view
: Default view type (list
,edit
ordata_import
: True, if data import is available.data_import
: True, if data export is available.data_columns
: List of columns that are exported.data_ignore
: List of columns that are not exported.data_map_fields
: Maps CSV file columns to internal columns when importing data.data_default_values
: Provides default values for columns during data import and export.data_id_field
: Specified an alternative primary key column when importing data.
The following sections will describe each individual listing option in more detail:
columns
Declares a list of columns that are presented by the corresponding model view in default list mode.
By default, the corresponding model view will extract a number of columns automatically if no columns have been declared.
Any column reference must be the name of
- a valid
model field
of the model or- an implicit method without arguments, like
get_FOO_display()
or- an explicit method without arguments, for example,
get_foo()
or- a property of the model, for example,
foo
The maximum number of columns is restricted to six full columns. Any declared column is a full column unless the column name is prefixed with
/
, in which case the column is presented as a half-column. Half columns are not as wide as full columns and the system can present any combination of half and full columns as long as the total number of full columns does not exceed six columns. Two half columns would make up for one full column.For example:
class Listing: columns = [ 'title', 'slug', 'isbn', 'published', '/author', # half column '/recommended', # half column '/price' # half column ]The column header label that is generated by the model view is automatically derived from the column reference name. For example, the column name
title
implicitly names the columnTitle
. You may declare the visual column label explicitly by piping the column name and the column label like in the following example:class Listing: columns = [ 'price|Total', ... ]The column value by default is left-aligned. However, you can align the column value to the right by prefixing the column name with a hyphen character (
-
):class Listing: columns = [ '-price|Total', # right-aligned ... ]For half-columns, the half-column indicator comes first, then the right-alignment indicator like the following example demonstrates:
class Listing: columns = [ '/-price|Total', # right-aligned half column ... ]There are multiple formatting options available which can be expressed as another pipe expression. Cubane supports the following formatting options:
- Boolean values presented as yes/no.
- Url (clickable website url).
- Currency.
- HTML content.
- Invoking listing action.
The following example gives an overview of all formatting options based on our book example:
class Listing: columns = [ 'get_absolute_url|Website|url', # website url 'html_excerpt|Excerpt|html', # html content 'recommended|Recommended|bool', # yes/no '-price|Total|currency', # right-aligned currency 'action:checkout' # invoke action 'checkout' ]All columns that directly map to a model field are sortable by that column. For example the column declaration
-price|Total|currency
directly maps to the model fieldprice
, therefore the user can sort the listing by theprice
column.If the column refers to a method or property, then sorting is disabled for such column. For example, the column declaration
get_absolute_url|Website|url
invokes the instance methodget_absolute_url
for each instance, therefore the resulting value is computed and the resulting listing cannot be sorted by this column.To overcome this limitation, the name of the underlying property may be declared as well which can then be used to sort the resulting list of items. For example, the column declaration
slug(get_absolute_url)|Website|url
is still invoking the instance methodget_absolute_url
for each instance but the resulting listing remains sortable by theWebsite
column based on sorting the internal columnslug
which – of course – is the basis by which the full URL is computed in the first place.A column declaration can also refer to foreign key fields. For example, let’s say the book class had a foreign key to an
Author
model which contained a name field, then the author’s name may be referenced in the listing by using the following column declaration:class Listing: columns = [ 'author__name|Author', # name field in Author model ... ]Note
The actual model view implementation may exclude certain columns to start with. Excluded columns will not be presented within the listing, even if the column is declared by the model.
edit_columns
Declares a list of columns that are presented by the corresponding model view in bulk editing mode.
By default, if no edit columns are declared, all regular columns declared via
columns
are used instead.class Listing: columns = [ 'title', 'slug', 'isbn', '/price' ] edit_view = True # enable bulk-editing to begin with edit_columns = [ # slug not editable in bulk editing mode... 'title', 'isbn', '/price' ]If columns are declared, then only those columns are presented in bulk editing mode. The same rules for declaring regular columns via
columns
applies when declaring columns for bulk editing.Note
Bulk editing must be enabled via the
edit_view
listing option in order to allow for bulk editing.Not all formatting options are available in bulk editing mode since all column data is presented as form elements rather than formatted column values.
Please refer to the
columns
section for more information on how to declare columns in general.
filter_by
Declares a list of columns that are presented within the side panel as filter options. By providing model fields as filter options, users of the backend system can precisely filter records by using specific values for individual columns.
By default, the filter panel is hidden away. Only if columns are declared via the
filter_by
option, the filter side panel is presented and available through the backend system.Each column must refer to a form field declared by the corresponding model form. This form is usually the same form that is used for editing records of the model through the backend system. An alternative form just for the purpose of filtering may be declared.
Filter columns are declared via the
filter_by
option. For example, the following declaration will allow books to be filtered by Title, Slug and/or ISDB number; provided that the model form provides those three fields.class Listing: filter_by = [ 'title', 'slug', 'isbn' ]The resulting filter side panel form will present those columns in the order as specified:
title
thenslug
thenisbn
. In particular, for filter forms containing a number of fields, section titles can be inserted in order to provide a sense of partition or to group similar fields:class Listing: filter_by = [ ':Book Title', # section title 'title', 'slug', ':Identification', # another section title 'isbn' ]In the example above, the resulting filter form still contains three form fields, but will contain grouping information that combines the form fields
title
andslug
into one local group (namedBook Title
) and places the form fieldisbn
into another logical group (namedIdentification
).Note
The system may change the default presentation of form fields to suit the purpose of the filter form. For example: Usually, boolean fields are represented as checkbox input fields within forms. However, the backend system will replace those with radio input fields for the purpose of filtering – providing three possible options: Off, Yes (
True
) and No (False
).
sortable
Declares if a model is sortable in the backend.
By default, a model is not sortable.
When making a model sortable, the backend system will provide user interface options to define the order or items by allowing users to drag and drop items. The established sorting order can then be used for example to derive the order in which items are presented on the frontend.
If sortable is set to ‘’True’‘, then the system implicitly expects that the model has a column with the name ‘’seq’ of some integer type, for example:
from django.db import models from cubane.models import DateTimeBase class Book(DateTimeBase): class Listing: sortable = True title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, db_index=True) isbn = models.CharField(max_length=32, db_index=True, unique=True) seq = models.IntegerField(db_index=True, default=0, editable=False)In this case, the model declares a field with the name ‘’seq’’ which is used by the system to store the order of books. The first book within a sequence will start with 1, the second book with 2 and so forth. Backend users can use drag ‘n drop in order to manipulate the order in which books are ordered.
On the frontend, you would usually sort books by ordering them by the ‘’seq’’ field; for example, to present a list of books in a specific user-defined order.
books = Book.objects.all().order_by('seq')The sequence of items is only defined per level of hierarchy in case folders are used. For example, if books were organised within folders - let’s say genres - then the sequence order as defined by the ‘’seq’’ field applies for each genre individually. Horror books declare a sequence starting with 1 while books in the kids section also start with 1. The backend system will distinguish those cases and will not allow users to change the order – books of multiple genres could be presented at the same time.
searchable
Declares a list of additional model fields by which model instances are searched when using the quick search facility.
By default, no additional model fields are declared as being searchable.
When searching via the quick search method, all visible columns as declared via the
columns
field are searchable. Further, quick search only applies to text-related fields and would not apply to boolean fields for example.Sometimes, you would like to allow the quick search facility to yield results based on columns that are to directly presented within the listing screen of the backend. In this case, those additional columns may be declared via the
searchable
listing option.For example, if the ISBN number for books would not be part of the listing for some reason, but you still wanted users to be able to search via the ISBN number in the backend, then you could declare the following listing option:
class Listing: searchable = ['isbn']Note
Non-text fields and fields that are already listed as columns via the
columns
listing option are ignored.
edit_view
Declares if bulk-editing is provided for the listing screen.
By default, bulk editing is not enabled.
Cubane’s backend system provides bulk editing functionality, where certain columns can be edited at the same time on the listing screen; rather than opening each item for editing individually.
This options is not enabled by default and may require the definition of columns that are applicable in bulk editing mode (see section
edit_columns
).
grid_view
Declares if the image-based grid view is provided for the listing screen.
By default, grid view is not available.
For some type of items, in particular, for those that have an image associated with it, Cubane’s backend system can present items as image thumbnails rather than tabular data. In particular, for media assets, this type of presentation is more useful.
The system will implicitly assume that each item is a
cubane.media.models.Media
instance or has a foreign key to aMedia
instance calledimage
.If your model does not have an image reference or the field is called differently, you can either rename your field or introduce a property with the name
image
that returns the reference toMedia
internally.class Listing: grid_view = True
default_view
Declares the default view that is presented for the corresponding model within Cubane’s backend system.
By default the regular list view is presented, which is always available for any model:
class Listing: default_view = 'grid'Available options are:
Option Description list
Default list view (tabular). compact-list
Compact version of the default list view (tabular). edit
Bulk editing view. grid
Image-based grid view
data_import
Declares whether CSV file data import is available or not.
By default, CSV data import is not available.
When importing CSV files, the first row must contain the name of the model field the column data represents and the model form must contain a valid form field of the same name.
During import, each data record is then processed via the model form as if the data were inputted into the form manually.
class Listing: data_import = TrueNote
If the import data contains the primary key field, then the corresponding record is updated; otherwise, a new record is created.
data_export
Declares whether CSV file data export is available or not.
By default, CSV data export is not available.
When exporting CSV files, the system will export all field columns, unless specific columns are declared via the
data_columns
listing option.class Listing: data_export = TrueNote
The system will automatically export model field names as the first data row that is exported.
data_columns
Declares a list of columns that are exported when using the CSV file export option (which must be enabled via
data_export
).By default, the list of exported columns is undefined and the exporter will export all model columns that are defined by the model automatically unless columns are ignored for data export via
data_ignore
.class Listing: data_export = True data_columns = [ 'id', 'title', 'slug', 'isbn' ]
data_ignore
Declares a list of columns that are ignored when export CSV files. Listed columns are not exported, even if they are declared via
data_columns
.By default, no columns are ignored.
class Listing: data_export = True data_ignore = [ 'isbn' # do not export ISBN ]Note
data_ignore
is ideally suited to declare fields you do not want to be exported if no list of columns has been declared viadata_columns
. In this case, all columns are exported and you can usedata_ignore
to remove unwanted ones.
data_map_fields
Declares a mapping from alternative column names to actual model field names used during the CSV file import process to understand differently-named columns.
By default, no additional data mapping is declared and the CSV file importer will only understand column names that match the model.
class Listing: data_export = True data_map_fields = { 'ISBN Number': 'isbn', ... }The example demonstrates how an additional column name
ISBN Number
may be declared in order to map the columnISBN Number
to the internal model fieldisbn
. Without such mapping, the column name must have been labelled asisbn
in order for the importer to be able to import the data successfully.Note
Mapping columns in this way is particularly useful if you renamed columns and would still be able to support the previous name or the input data is part of an initial data import process and has been compiled by hand with column labels that do not necessarily match the ones used by the model.
data_default_values
Declares a default value that is used when importing data from a CSV file and a particular column cell value happen to be empty.
By default, the default value for an empty cell value is
None
, unlessdata_default_value
declares a different value.class Listing: data_export = True data_default_values = { 'title': '<Unnamed>', ... }The example demonstrates how a book title will automatically take the value
<Unnamed>
in case a book title is not defined within the CSV import file.Note
Even though the default value is
None
, the actual value that is used by the model may also depend on the model form that is used by the data importer to validate each record. Further, the model itself may declare a default value on the database level.The listing option
data_default_values
sets a default value as part of the import process before the data is validated via the form or saved to the database.
data_id_field
Declares the name of the column that is used as the primary key for each record in order for the data importer to decide if any given record already exists in the system and is therefore considered to be an update operation rather than an insert operation.
By default, the name of the primary key column is the name of the primary key of the model, which is typically
id
but does not have to be.class Listing: data_export = True data_id_field = 'isbn'The example above shows how books are mapped to all existing books in the system when importing CSV data from a file. By default, the system would have used the
id
primary key column of the model for this purpose, but perhaps we do not want to include the built-inid
column in the first place. By declaring theisbn
column as the primary key, any existing records from the CSV file with the sameisbn
number will trigger a data update rather than an insert.Note
Make sure that the column that has been declared as the primary key for the purpose of importing data from a CSV file is unique. There should not be more than one record with the same column value.
Model View Overrides¶
A model base class may specify backend-specific view options which are then inherited to its parent model classes and can be overridden fully or partially.
For example, the following model may declare some general view options:
from cubane.models import DateTimeBase
class BaseModel(DateTimeBase):
class Listing:
columns = [
'foo',
'bar'
]
sortable = False
Then a deriving class may add the ability to sort items:
from django.db import models
from cubane.models import DateTimeBase
class Book(BaseModel):
class Listing:
sortable = True
seq = models.IntegerField(db_index=True, default=0, editable=False)
In order to provide such functionality through the backend system, the
sortable
view option is overridden. However, the columns
view option has not been overridden; therefore the declaration from the base
class is used instead.