Extend the data model

The Data model can be extended in the file <package>/models.py and the corresponding admin interface configuration in the file <package>/forms.py.

For example to add some user details, including a link to a new class named “Title”, add to <package>/models.py:

# Used to hide the original user in the admin interface
User.__acl__ = [DENY_ALL]

class UserDetail(User):
    __label__ = _('userdetail')
    __plural__ = _('userdetails')
    __tablename__ = 'userdetail'
    __table_args__ = {'schema': _schema}
    __acl__ = [
        (Allow, Authenticated, ALL_PERMISSIONS),
    ]
    __mapper_args__ = {'polymorphic_identity': 'detailed'}
    id = Column(types.Integer, ForeignKey(_schema + '.user.id'),
            primary_key=True)

    phone = Column(types.Unicode, nullable=False, label=_(u'phone'))

    # title
    title_id = Column(Integer, ForeignKey(_schema + '.title.id'), nullable=False)
    title = relationship("Title", backref=backref('users'))

    def __init__(self, username=u'', password=u'', functionalities=[],
                 phone=u'', email=u'', title=None, role=None):
        User.__init__(self, username, password, email, functionalities, role)
        self.phone = phone
        self.title = title

class Title(Base):
    __label__ = _('title')
    __plural__ = _('titles')
    __tablename__ = 'title'
    __table_args__ = {'schema': _schema}
    __acl__ = [
        (Allow, Authenticated, ALL_PERMISSIONS),
    ]
    id = Column(types.Integer, primary_key=True)
    name = Column(types.Unicode, nullable=False, label=_(u'Name'))
    description = Column(types.Unicode, label=_(u'Description'))

    def __init__(self, name=u'', description=u''):
        self.name = name
        self.description = description

    def __unicode__ (self):
        return self.name or u''

And in the file <package>/forms.py:

# Add a field set (form) for the title
Title = FieldSet(models.Title)

# Add a field set for the user details
UserDetail = FieldSet(models.UserDetail)
# Need to hide the password
password = forms.DblPasswordField(UserDetail, UserDetail._password)
UserDetail.append(password)
# Fix the fields order
fieldOrder = [UserDetail.username.validate(forms.unique_validator)
                               .with_metadata(mandatory=''),
              password, UserDetail.role]
if hasattr(UserDetail, 'parent_role'):
    fieldOrder.append(UserDetail.parent_role)
fieldOrder.extend([UserDetail.title,
        UserDetail.functionalities.
                set(renderer=forms.FunctionalityCheckBoxTreeSet)])
UserDetail.configure(include=fieldOrder)

# Add a grid for the title
TitleGrid = Grid(models.Title)

# Add a grid for the  user details
UserGrid = Grid(models.UserDetail)
# Visible fields
fieldOrder = [UserDetail.username,
              UserDetail.title,
              UserDetail.functionalities,
              UserDetail.role]
if hasattr(UserGrid, 'parent_role'):
    fieldOrder.append(UserDetail.parent_role)
UserGrid.configure(include=fieldOrder)

We can change the renderer of a field, for example change the Role renderer:

Role.name.set(renderer=AnOtherRenderer, ...)

And if we need an other resource (javascript or stylesheet):

from fa.jquery import fanstatic_resources
from fanstatic import Resource, Group, Library
from pyramid_formalchemy import events as fa_events

fanstatic_lib = Library('<package>_admin', 'static')
new_js = Resource(fanstatic_lib, '<path_to_javascript.js>',
    depends=[fanstatic_resources.<depends_on>])
new_css = Resource(fanstatic_lib, '<path_to_stylesheet.css>')

@fa_events.subscriber([models.Role, fa_events.IBeforeRenderEvent])
def before_render_role(context, event):
    Group([new_js, new_css]).need()

And in the setup.py we need to add in the entry_points:

'fanstatic.libraries': [
    '<package>_admin = <package>.forms:fanstatic_lib',
],