Working with the TurboGears Admin

TurboGears provides the tgext.admin extension which is powered by tgext.crud and sprox. This can be used to automatically create simple administration pages and is the toolkit powering the /admin page in newly quickstarted applications.

By default the admin will provide autogenerated access to all the models imported in your project models/__init__.py.

While the default configuration for the admin usually just works it doesn’t provide the best experience for users and might require some customizations.

To do so the TurboGears Admin permits to provide custom configurations throught the AdminController config_type paramter.

The default turbogears admin is created as:

admin = AdminController(model, DBSession, config_type=TGAdminConfig)

which creates an admin for all the models with the default TurboGears admin configuration.

Restricting Access to some Models

Restricting access to some models is possible by specifying them explicitly instead of passing model as the first argument to the AdminController:

from myproject.model import User, Group

admin = AdminController([User, Group], DBSession, config_type=TGAdminConfig)

Switching to a Custom Admin Configuration

First step to perform if you want to switch to a customized administration configuration is to declare your own config. This can easily be done by subclassing TGAdminConfig:

class CustomAdminConfig(TGAdminConfig):
    pass

if you replace config_type=TGAdminConfig with your new CustomAdminConfig:

admin = AdminController(model, DBSession, config_type=CustomAdminConfig)

You will notice that everything works as before, as we didn’t change the configuration in any way. The custom configuration will contains properties for the admin itself and for each CRUD inside the admin.

Apart from configuration for each crud, there are some general admin configuration options:

class tgext.admin.config.AdminConfig(models, translations=None)
Class attributes:
 
layout

The look and feel of the admin. Three builtin layouts are available: tgext.admin.layouts.BasicAdminLayout, tgext.admin.layouts.BootstrapAdminLayout and tgext.admin.layouts.GroupedBootstrapAdminLayout.

Note

GroupedBootstrapAdminLayout only works with the Genshi and Kajiki template languages, so it cannot be used when quickstarting with Jinja or Mako.

project_name

Name of the project, by default is same as config[‘package_name’].

index_payoff

Custom payoff to display into the admin index page.

default_index_template

This is the template of the /admin page, by default the one specified inside the layout is used.

allow_only

Predicate to restrict access to the whole admin. By default in_group('managers') is used.

include_left_menu

bool that states if the sidebar should be included in admin pages or not. By default the sidebar is visible, please note that hiding the sidebar might break the admin layout.

DefaultControllerConfig

The default CRUD configuration for models that do not provide a custom CRUD configuration. By default tgext.admin.config.CrudRestControllerConfig

Customizing Models CRUD

The admin page can be configured using the TGAdminConfig class, supposing we have a game with running Match and a list of Settings we can declared MatchAdminController and SettingAdminController which inherit from EasyCrudRestController and tell TurboGears Admin to use them for the administration of matches and settings:

class CustomAdminConfig(TGAdminConfig):
    class match(CrudRestControllerConfig):
        defaultCrudRestController = MatchAdminController
    class setting(CrudRestControllerConfig):
        defaultCrudRestController = SettingAdminController

class RootController(BaseController):
    admin = AdminController([model.Match, model.Setting], DBSession,
                            config_type=CustomAdminConfig)

This will create an administration controller which uses our custom CrudRestControllers to manage Match and Settings instances.

Each class attribute of our CustomAdminConfig that has the lower case name of a Model will be used to configure the admin CRUD for that model.

This can be done by subclassing CrudRestControllerConfig with a defaultCrudRestController class attribute that points to the CrudRestController class to use for the related model.

Defining Custom Controllers

The explicit way to define models configuration is by declaring CrudRestControllers and configurations separately, but in case the custom controllers are required only for the admin it might be shortest to just define them together:

class MyModelAdminCrudConfig(CrudRestControllerConfig):
    class defaultCrudRestController(EasyCrudRestController):
        __table_options__ = {
            # options here...
        }

class CustomAdminConfig(TGAdminConfig):
    photo = MyModelAdminCrudConfig

For a complete list of options available inside defaultCrudRestController refer to TurboGears Automatic CRUD Generation.

Photos Admin Tutorial

Now suppose we have a photo model that looks like:

class Photo(DeclarativeBase):
    __tablename__ = 'photos'

    uid = Column(Integer, primary_key=True)
    title = Column(Unicode(255), nullable=False)
    image = Column(LargeBinary, nullable=False)
    mimetype = Column(Unicode(64), nullable=False, default='image/jpeg')

    user_id = Column(Integer, ForeignKey('tg_user.user_id'), index=True)
    user = relationship('User', uselist=False,
                        backref=backref('photos',
                                        cascade='all, delete-orphan'))

we might want to customize our admin to use the GroupedBootstrapAdminLayout layout, put the photos inside the Media group and improve the table view for the crud by removing the user_id and mimetype columns and declare the image column as xml so that we can show the image inside:

import base64
from tgext.admin import CrudRestControllerConfig
from tgext.admin.tgadminconfig import BootstrapTGAdminConfig as TGAdminConfig
from tgext.admin.layouts import GroupedBootstrapAdminLayout
from tgext.admin.controller import AdminController as TGAdminController
from tgext.crud import EasyCrudRestController


class PhotoAdminCrudConfig(CrudRestControllerConfig):
    icon_class = 'glyphicon-picture'
    admin_group = 'Media'

    class defaultCrudRestController(EasyCrudRestController):
        __table_options__ = {
            '__omit_fields__': ['user_id', 'mimetype'],
            '__xml_fields__': ['image'],

            'image': lambda filler, row: '<img src="data:%s;base64,%s" width="256"/>' % (
                row.mimetype,
                base64.b64encode(row.image).decode('ascii')
            )
        }


class CustomAdminConfig(TGAdminConfig):
    layout = GroupedBootstrapAdminLayout

    photo = PhotoAdminCrudConfig

Now our admin works as expected and uses the PhotoAdminCrudConfig to manage photos. There is still an open issue, when uploading photos we have to manually provide the photo user and mimetype.

To solve this issue we will customize the form hiding the two values from the form and forcing them when data is submitted:

import base64, imghdr
from tg import expose, request
from tgext.admin import CrudRestControllerConfig
from tgext.admin.tgadminconfig import BootstrapTGAdminConfig as TGAdminConfig
from tgext.admin.layouts import GroupedBootstrapAdminLayout
from tgext.admin.controller import AdminController as TGAdminController
from tgext.crud import EasyCrudRestController


class PhotoAdminCrudConfig(CrudRestControllerConfig):
    icon_class = 'glyphicon-picture'
    admin_group = 'Media'

    class defaultCrudRestController(EasyCrudRestController):
        __table_options__ = {
            '__omit_fields__': ['user_id', 'mimetype'],
            '__xml_fields__': ['image'],

            'image': lambda filler, row: '<img src="data:%s;base64,%s" width="256"/>' % (
                row.mimetype,
                base64.b64encode(row.image).decode('ascii')
            )
        }

        __form_options__ = {
            '__hide_fields__': ['user', 'mimetype']
        }

        @expose(inherit=True)
        def post(self, *args, **kw):
            kw['user'] = request.identity['user'].user_id
            kw['mimetype'] = 'image/%s' % imghdr.what(kw['image'].file)
            return EasyCrudRestController.post(self, *args, **kw)

        @expose(inherit=True)
        def put(self, *args, **kw):
            image = kw.get('image')
            if image is not None and image.filename:
                kw['mimetype'] = 'image/%s' % imghdr.what(kw['image'].file)
            return EasyCrudRestController.put(self, *args, **kw)


class CustomAdminConfig(TGAdminConfig):
    layout = GroupedBootstrapAdminLayout

    photo = PhotoAdminCrudConfig