Customizing views (how models are displayed)

As it has been said before, the view layer is not obliged to render any specific widget for each kind of field, however, we can also override which widget will be used for each of the model’s fields or the widget for the whole model.

To do this we need to lose some generality and instead of using the base rum.view.ViewFactory we need to use the specific view factory of the view plugin we’re using. This is because each view factory generates widgets/views for each field that might not be possible to aggregate into a compound view for the whole model if all of the widgets are not of the same “family”.

Registering a view for a model

Views are registered in a factory using the same API as in the rest of the factories:

from tw.rum import WidgetFactory
from somewhere import MovieForm

# Remember to register the form for both pair of related actions
WidgetFactory.register(MovieForm(), Movie, action='edit')
WidgetFactory.register(MovieForm(), Movie, action='update')

Using Sprox to generate forms and registering them with Rum

If you’re using the tw.rum/RumAlchemy combination to generate RESTful admin interfaces for your SQLAlchemy mapped classes you can also use Sprox to generate forms since Sprox generates compatible tw.forms.

Example:

from sprox.formbase import AddRecordForm
from formencode import Schema
from formencode.validators import FieldsMatch
from tw.forms import PasswordField, TextField
# We're assuming a TG2 app
from yourapp.model import DBSession, User

form_validator =  Schema(
    chained_validators = [
        FieldsMatch('password', 'verify_password',
                    messages={'invalidNoMatch': 'Passwords do not match'})
        ]
    )

class RegistrationForm(AddRecordForm):
    __model__ = User
    __require_fields__     = ['password', 'user_name', 'email_address']
    __omit_fields__        = ['_password', 'groups', 'created', 'user_id',
                              'town_id']
    __field_order__        = ['user_name', 'email_address', 'display_name',
                              'password', 'verify_password']
    __base_validator__     = form_validator
    email_address          = TextField
    display_name           = TextField
    verify_password        = PasswordField('verify_password')

Once you have your declared your RegistrationForm you should register it with Rum so it knows it should use it for the new/create actions of User:

from tw.rum import WidgetFactory
register_form = RegistrationForm()
# Remember to register the form for both pair of related actions
WidgetFactory.register(register_form, User, action='new')
WidgetFactory.register(register_form, User, action='create')

Overriding the widget/view for a single field

Ok, say we want to use a custom calendar widget we’ve written to edit the filmed_on field:

.. code-block:: python
from somewhere import FanciestCalendar from tw.rum.viewfactory import WidgetFactory from model import Movie WidgetFactory.register(FanciestCalendar(), Movie, attr=’filmed_on’, action=’edit’)

Notice the attr keyword argument which specifies the name of the field the rule that will be generated should match..

What effect does this have?

By overriding the whole view of a model we can control exactly how an instance will be displayed, edited and validated. Note that we need to override the complementary actions (new/create, edit/update) since the same view, or another but compatible view, will be used to validate the input generated when the form was submitted from the browser.