2012-08-30 08:44:17 +02:00
|
|
|
# Gridfield
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Gridfield is SilverStripe's implementation of data grids. Its main purpose is to display tabular data
|
|
|
|
in a format that is easy to view and modify. It's a can be thought of as a HTML table with some tricks.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
It's built in a way that provides developers with an extensible way to display tabular data in a
|
|
|
|
table and minimise the amount of code that needs to be written.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
In order to quickly get data-focused UIs up and running,
|
|
|
|
you might also be interested in the [/reference/modeladmin](ModelAdmin) class
|
|
|
|
which is driven largely by the `GridField` class explained here.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## Overview
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The `GridField` is a flexible form field for creating tables of data. It was introduced in
|
|
|
|
SilverStripe 3.0 and replaced the `ComplexTableField`, `TableListField`, and `TableField` from
|
|
|
|
previous versions of SilverStripe.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Each GridField is built from a number of components. Without any components, a GridField has almost no
|
|
|
|
functionality. The components are responsible for formatting data to be readable and also modifying it.
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A gridfield with only the `GridFieldDataColumn` component will display a set of read-only columns
|
|
|
|
taken from your list, without any headers or pagination. Large datasets don't fit to one
|
|
|
|
page, so you could add a `GridFieldPaginator` to paginatate the data. Sorting is supported by adding
|
|
|
|
a `GridFieldSortableHeader` that enables sorting on fields that can be sorted.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
This document aims to explain the usage of GridFields with code examples.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
<div class="hint" markdown='1'>
|
|
|
|
GridField can only be used with datasets that are of the type `SS_List` such as `DataList`
|
|
|
|
or `ArrayList`
|
|
|
|
</div>
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## Creating a base GridField
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A gridfield is often setup from a `Controller` that will output a form to the user. Even if there
|
|
|
|
are no other HTML input fields for gathering data from users, the gridfield itself must have a
|
|
|
|
`Form` to support interactions with it.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Here is an example where we display a basic gridfield with the default settings:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
|
|
|
:::php
|
2012-08-30 08:44:17 +02:00
|
|
|
class GridController extends Page_Controller {
|
2013-01-28 22:35:32 +01:00
|
|
|
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $allowed_actions = array('index');
|
2012-08-30 08:44:17 +02:00
|
|
|
|
|
|
|
public function index(SS_HTTPRequest $request) {
|
|
|
|
$this->Content = $this->AllPages();
|
|
|
|
return $this->render();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function AllPages() {
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get());
|
|
|
|
return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
|
|
|
|
}
|
|
|
|
}
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
__Note:__ This is example code and the gridfield might not be styled nicely depending on the rest of
|
|
|
|
the css included.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
This gridfield will only contain a single column with the `Title` of each page. Gridfield by default
|
|
|
|
uses the `DataObject::$display_fields` for guessing what fields to display.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Instead of modifying a core `DataObject` we can tell the gridfield which fields to display by
|
|
|
|
setting the display fields on the `GridFieldDataColumns` component.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
|
|
|
:::php
|
2012-08-30 08:44:17 +02:00
|
|
|
public function AllPages() {
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get());
|
|
|
|
$dataColumns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
|
|
|
|
$dataColumns->setDisplayFields(array(
|
|
|
|
'Title' => 'Title',
|
|
|
|
'URLSegment'=> 'URL',
|
|
|
|
'LastEdited' => 'Changed'
|
|
|
|
));
|
|
|
|
return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
|
|
|
|
}
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
We will now move onto what the `GridFieldConfig`s are and how to use them.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
----
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
## Configuration
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A gridfields's behaviour and look all depends on what config we're giving it. In the above example
|
|
|
|
we did not specify one, so it picked a default config called `GridFieldConfig_Base`.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A config object is a container for `GridFieldComponents` which contain the actual functionality and
|
|
|
|
view for the gridfield.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A config object can be either injected as the fourth argument of the GridField constructor,
|
|
|
|
`$config` or set at a later stage by using a setter:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
|
|
|
// On initialisation:
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
|
|
|
|
// By a setter after initialisation:
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get());
|
|
|
|
$gridField->setConfig(GridFieldConfig_Base::create());
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The framework comes shipped with some base GridFieldConfigs:
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
### Table listing with GridFieldConfig_Base
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A simple read-only and paginated view of records with sortable and searchable headers.
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The fields displayed are from `DataObject::getSummaryFields()`
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
### Viewing records with GridFieldConfig_RecordViewer
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Similar to `GridFieldConfig_Base` with the addition support of:
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- View read-only details of individual records.
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The fields displayed in the read-only view is from `DataObject::getCMSFields()`
|
2012-03-05 12:27:25 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordViewer::create());
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
### Editing records with GridFieldConfig_RecordEditor
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Similar to `GridFieldConfig_RecordViewer` with the addition support of:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- Viewing and changing an individual records data.
|
|
|
|
- Deleting a record
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordEditor::create());
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The fields displayed in the edit form are from `DataObject::getCMSFields()`
|
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
### Editing relations with GridFieldConfig_RelationEditor
|
2012-03-01 14:43:42 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or
|
2012-10-27 00:30:26 +02:00
|
|
|
many-many relationships. As such, it expects the list used with the `GridField` to be a
|
|
|
|
`RelationList`. That is, the list returned by a has-many or many-many getter.
|
2012-03-01 14:43:42 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The relations can be:
|
2012-03-01 14:43:42 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- Searched for existing records and add a relationship
|
|
|
|
- Detach records from the relationship (rather than removing them from the database)
|
2012-10-27 00:30:26 +02:00
|
|
|
- Create new related records and automatically add them to the relationship.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
2012-10-27 00:30:26 +02:00
|
|
|
$gridField = new GridField('images', 'Linked images', $this->Images(), GridFieldConfig_RelationEditor::create());
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
The fields displayed in the edit form are from `DataObject::getCMSFields()`
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
## Customizing Detail Forms
|
|
|
|
|
|
|
|
The `GridFieldDetailForm` component drives the record editing form which is usually configured
|
|
|
|
through the configs `GridFieldConfig_RecordEditor` and `GridFieldConfig_RelationEditor`
|
|
|
|
described above. It takes its fields from `DataObject->getCMSFields()`,
|
|
|
|
but can be customized to accept different fields via its `[api:GridFieldDetailForm->setFields()](api:setFields())` method.
|
|
|
|
|
|
|
|
The component also has the ability to load and save data stored on join tables
|
|
|
|
when two records are related via a "many_many" relationship, as defined through
|
|
|
|
`[api:DataObject::$many_many_extraFields]`. While loading and saving works transparently,
|
|
|
|
you need to add the necessary fields manually, they're not included in the `getCMSFields()` scaffolding.
|
|
|
|
|
|
|
|
These extra fields act like usual form fields, but need to be "namespaced"
|
|
|
|
in order for the gridfield logic to detect them as fields for relation extradata,
|
|
|
|
and to avoid clashes with the other form fields.
|
|
|
|
The namespace notation is `ManyMany[<extradata-field-name>]`, so for example
|
|
|
|
`ManyMany[MyExtraField]`.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class Player extends DataObject {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $db = array('Name' => 'Text');
|
2012-11-08 01:26:38 +01:00
|
|
|
public static $many_many = array('Teams' => 'Team');
|
|
|
|
public static $many_many_extraFields = array(
|
|
|
|
'Teams' => array('Position' => 'Text')
|
|
|
|
);
|
|
|
|
public function getCMSFields() {
|
|
|
|
$fields = parent::getCMSFields();
|
|
|
|
|
|
|
|
if($this->ID) {
|
|
|
|
$teamFields = singleton('Team')->getCMSFields();
|
|
|
|
$teamFields->addFieldToTab(
|
|
|
|
'Root.Main',
|
|
|
|
// Please follow the "ManyMany[<extradata-name>]" convention
|
|
|
|
new TextField('ManyMany[Position]', 'Current Position')
|
|
|
|
);
|
|
|
|
$config = GridFieldConfig_RelationEditor::create();
|
|
|
|
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
|
|
|
|
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
|
|
|
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Team extends DataObject {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $db = array('Name' => 'Text');
|
2012-11-08 01:26:38 +01:00
|
|
|
public static $many_many = array('Players' => 'Player');
|
|
|
|
}
|
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## GridFieldComponents
|
2012-01-09 05:57:05 +01:00
|
|
|
|
2012-11-08 01:26:38 +01:00
|
|
|
The `GridFieldComponent` classes are the actual workers in a gridfield. They can be responsible for:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- Output some HTML to be rendered
|
|
|
|
- Manipulate data
|
|
|
|
- Recieve actions
|
|
|
|
- Display links
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Components are added and removed from a config by setters and getters.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
|
|
|
:::php
|
2012-08-30 08:44:17 +02:00
|
|
|
$config = GridFieldConfig::create();
|
2012-03-08 07:04:07 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
// Add the base data columns to the gridfield
|
|
|
|
$config->addComponent(new GridFieldDataColumns());
|
|
|
|
$gridField = new GridField('pages', 'All pages', SiteTree::get(), $config);
|
2012-03-08 07:04:07 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
It's also possible to insert a component before another component.
|
|
|
|
|
2012-03-08 07:04:07 +01:00
|
|
|
:::php
|
2012-08-30 08:44:17 +02:00
|
|
|
$config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
|
2012-03-08 07:04:07 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Adding multiple components in one call:
|
2012-03-08 07:04:07 +01:00
|
|
|
|
|
|
|
:::php
|
2012-08-30 08:44:17 +02:00
|
|
|
$config->addComponents(new GridFieldDataColumns(), new GridFieldToolbarHeader());
|
2012-03-08 07:04:07 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Removing a component:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
:::php
|
|
|
|
$config->removeComponentsByType('GridFieldToolbarHeader');
|
2012-01-09 05:57:05 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
For more information, see the [API for GridFieldConfig](http://api.silverstripe.org/3.0/framework/GridFieldConfig.html).
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Here is a list of components for generic use:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- `[api:GridFieldToolbarHeader]`
|
|
|
|
- `[api:GridFieldSortableHeader]`
|
|
|
|
- `[api:GridFieldFilterHeader]`
|
|
|
|
- `[api:GridFieldDataColumns]`
|
|
|
|
- `[api:GridFieldDeleteAction]`
|
|
|
|
- `[api:GridFieldViewButton]`
|
|
|
|
- `[api:GridFieldEditButton]`
|
|
|
|
- `[api:GridFieldPaginator]`
|
|
|
|
- `[api:GridFieldDetailForm]`
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## Creating a custom GridFieldComponent
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A single component often uses a number of interfaces.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
### GridField_HTMLProvider
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Provides HTML for the header/footer rows in the table or before/after the template.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Examples:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- A header html provider displays a header before the table
|
|
|
|
- A pagination html provider displays pagination controls under the table
|
|
|
|
- A filter html fields displays filter fields on top of the table
|
|
|
|
- A summary html field displays sums of a field at the bottom of the table
|
|
|
|
|
|
|
|
### GridField_ColumnProvider
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Add a new column to the table display body, or modify existing columns. Used once per record/row.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Examples:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- A data columns provider that displays data from the list in rows and columns.
|
|
|
|
- A delete button column provider that adds a delete button at the end of the row
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
### GridField_ActionProvider
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Action providers runs actions, some examples are:
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- A delete action provider that deletes a DataObject.
|
|
|
|
- An export action provider that will export the current list to a CSV file.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
|
|
|
### GridField_DataManipulator
|
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Modifies the data list. In general, the data manipulator will make use of `GridState` variables
|
|
|
|
to decide how to modify the data list.
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Examples:
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- A paginating data manipulator can apply a limit to a list (show only 20 records)
|
|
|
|
- A sorting data manipulator can sort the Title in a descending order.
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
### GridField_URLHandler
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Sometimes an action isn't enough, we need to provide additional support URLs for the grid. It
|
|
|
|
has a list of URL's that it can handle and the GridField passes request on to URLHandlers on matches.
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Examples:
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
- A pop-up form for editing a record's details.
|
|
|
|
- JSON formatted data used for javascript control of the gridfield.
|
2012-01-09 06:47:31 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## GridField_FormAction
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
This object is used for creating actions buttons, for example a delete button. When a user clicks on
|
|
|
|
a FormAction, the gridfield finds a `GridField_ActionProvider` that listens on that action.
|
|
|
|
`GridFieldDeleteAction` have a pretty basic implementation of how to use a Form action.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2013-01-15 12:41:25 +01:00
|
|
|
## GridField_SaveHandler
|
|
|
|
|
|
|
|
This is used to create a handler that is called when a form containing the grid
|
|
|
|
field is saved into a record. This is useful for performing actions when saving
|
|
|
|
the record.
|
|
|
|
|
2012-01-09 05:39:18 +01:00
|
|
|
### GridState
|
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
Gridstate is a class that is used to contain the current state and actions on the gridfield. It's
|
|
|
|
transfered between page requests by being inserted as a hidden field in the form.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
A GridFieldComponent sets and gets data from the GridState.
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-12-17 00:46:51 +01:00
|
|
|
## Permissions
|
|
|
|
|
|
|
|
Since GridField is mostly used in the CMS, the controller managing a GridField instance
|
|
|
|
will already do some permission checks for you, and can decline display or executing
|
|
|
|
any logic on your field.
|
|
|
|
|
|
|
|
If you need more granular control, e.g. to consistently deny non-admins from deleting
|
|
|
|
records, use the `DataObject->can...()` methods
|
|
|
|
(see [DataObject permissions](/reference/dataobject#permissions)).
|
|
|
|
|
2012-08-30 08:44:17 +02:00
|
|
|
## Related
|
2012-01-09 05:39:18 +01:00
|
|
|
|
2012-10-27 00:30:26 +02:00
|
|
|
* [ModelAdmin: A UI driven by GridField](/reference/modeladmin)
|
|
|
|
* [Tutorial 5: Dataobject Relationship Management](/tutorials/5-dataobject-relationship-management)
|