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,editorgrid).default_view: Default view type (list,editordata_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 fieldof 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,
fooThe 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
titleimplicitly 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|currencydirectly maps to the model fieldprice, therefore the user can sort the listing by thepricecolumn.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|urlinvokes the instance methodget_absolute_urlfor 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|urlis still invoking the instance methodget_absolute_urlfor each instance but the resulting listing remains sortable by theWebsitecolumn based on sorting the internal columnslugwhich – 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
Authormodel 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
columnsare 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
columnsapplies when declaring columns for bulk editing.Note
Bulk editing must be enabled via the
edit_viewlisting 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
columnssection 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_byoption, 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_byoption. 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:
titlethenslugthenisbn. 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
titleandsluginto one local group (namedBook Title) and places the form fieldisbninto 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
columnsfield 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
searchablelisting 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
columnslisting 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.Mediainstance or has a foreign key to aMediainstance 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
imagethat returns the reference toMediainternally.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 listDefault list view (tabular). compact-listCompact version of the default list view (tabular). editBulk editing view. gridImage-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_columnslisting 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_ignoreis 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_ignoreto 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 Numbermay be declared in order to map the columnISBN Numberto the internal model fieldisbn. Without such mapping, the column name must have been labelled asisbnin 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_valuedeclares 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_valuessets 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
idbut 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
idprimary key column of the model for this purpose, but perhaps we do not want to include the built-inidcolumn in the first place. By declaring theisbncolumn as the primary key, any existing records from the CSV file with the sameisbnnumber 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.