# 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:

## 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