pythonformsweb-frameworksturbogears2toscawidgets

TurboGears2 - how to customize layout for new user form?


I am currently using this for my new user form:

class UserForm(AdminPage):
    entity = Model.User
    title = 'User'
    class child(ListForm):
        css_class = 'form-horizontal'
        buttons = [SaveButton(),CancelButton()]
        ...
        phone = TextField(
                label = 'Phone',
                validator = twc.Required
        )
        ...

I am assuming that I will have to use something else than a ListForm to do what I want. Here is what I need:

I would like to customize the length of certain form fields, put two form fields next to one another rather than below and change the label on these two fields to appear above the fields rather then on their left.

I spent hours wading through the different versions of tg docs and the 1.0 API, but I could find nothing that worked. I tried adding:

__field_attrs__={'phone':{'rows':'2'}}

but nothing changed. I am assuming a ListForm does not have field_attrs!? Can anyone point me in the right direction on how to achieve what I am looking for?

Thanks a lot!


Solution

  • You can add CSS classes and styles to your fields like so:

    phone = TextField(label='Phone',
                      validator=twc.Required,
                      css_class='MyTextFieldClass',
                      attrs=dict(style='display:block;width:8em', maxlength='12'))
    

    For a completely different layout, you need to subclass BaseLayout and reference your own template as explained here: http://tw2core.readthedocs.org/en/latest/design/#template.

    For instance, I have created a more flexible Layout class called FloatLayout:

    from itertools import groupby
    from tw2.core import Param
    from tw2.forms.widgets import BaseLayout
    
    class FloatLayout(BaseLayout):
    
        template = "widgets.templates.float_layout"
    
        aside = Param('All fields aside', default=False)
    
        def rows(self, children):
            """Create the rows."""
            def row_no(child, no=[0]):
                if not self.aside and not getattr(child, 'aside', False):
                    no[0] += 1
                return no[0]
            return groupby(children, row_no)
    

    It can be used with this FloatForm class:

    from tw2.core import Variable
    from tw2.forms import Form
    
    class FloatForm(Form):
        """Form using floating divisions allowing multiple fields per row.
    
        Fields having the 'aside' attribute set appear on the same row.
    
        Something like the following should be included in the site CSS file:
    
        form.floatform {
            margin-bottom: 1ex;
        }
        form.floatform div.row {
            clear: left;
            overflow: hidden;
            height: 100%;
            margin-top: 1.5ex;
        }
        form.floatform div.field {
            float: left;
            margin-right: 1em;
        }
        form.floatform label.fieldlabel {
            display: block;
        }
        form.floatform div.submit {
            margin-top: 3ex;
        }
    
        """
    
        template = "widgets.templates.float_form"
    
        child = Variable(default=FloatLayout)
    
        css_class = "floatform"
    

    The Genshi template float_layout.html for the FloatLayout is this:

    <div xmlns:py="http://genshi.edgewall.org/" py:attrs="w.attrs" py:strip="True">
        <div py:for="row_no, row in w.rows(w.children_non_hidden)"
                class="${row_no % 2 and 'odd' or 'even'} row">
            <div py:for="child in row" py:attrs="child.container_attrs"
                class="field${child.validator and
                    getattr(child.validator, 'required', None) and ' required' or ''}"
                title="${w.hover_help and w.help_text or ''}">
                <label py:if="child.label" for="${child.attrs.get('id')}"
                    class="fieldlabel" py:content="child.label"/>
                <span py:replace="child.display()"/>
                <span py:if="not w.hover_help and child.help_text"
                    class="fieldhelp" py:content="child.help_text"/>
                <span py:if="child.error_msg"
                    class="fielderror" py:content="child.error_msg"/>
            </div>
        </div>
        <div py:if="w.children_hidden" style="display:none">
            <div py:for="child in w.children_hidden" py:replace="child.display()"/>
        </div>
    </div>
    

    The Genshi template float_form.html for the FloatForm is this:

    <form xmlns:py="http://genshi.edgewall.org/"
        class="floatform" py:attrs="w.attrs">
        <div py:if="w.error_msg" class="formerror" py:content="w.error_msg"/>
        <div py:if="w.help_msg" class="formhelp"><p py:content="w.help_msg"/></div>
        <div py:replace="w.child.display()"/>
        <div py:for="button in w.buttons" class="field" py:content="button.display()"/>
    </form>
    

    A concrete Form could now look like this:

    class UserForm(FloatForm):
        action = url('save_user')
        submit = SubmitButton('Save user')
        user_id = HiddenField(validator=IntValidator())
        user_name = TextField(validator=UserNameValidator(max=16),
            size=20, maxlength=16, label=u'User name:')
        remote_account = CheckBox(validator=BoolValidator(),
            label=u'Remote account:', aside=True)
        new_password = PasswordField(
            validator=PasswordValidator(required=False),
            size=20, maxlength=16, label=u'Password:', aside=True)
        group_id = CheckBoxList(item_validator=IntValidator(),
            label=u'Roles:', css_class='inline')
        display_name = TextField(validator=NameValidator(max=255),
            size=64, maxlength=255, label=u'Real name:')
        mail = TextField(validator=EmailIfLocalValidator(),
            size=64, maxlength=255, label=u'Email address:')
    

    As you see, the fields remote_account, new_password have an attribute aside which causes them to appear on the same line as user_name.