Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WTForms - display property value instead of HTML field

I want to re-use a template I have with my WTForms form:

<th>${form.name.label}</th>
<td>${form.name()}</td>
...

However, on my edit page, I want the input fields to display as normal (TextField, SelectField, etc.), while on my view page, I want to just display the value of the property, not the input field with the value.

Edit page:

<th>Name:</th>
<td><input type="text" value="Current Name" name="name" id="name"/></td>

View page:

<th>Name:</th>
<td>Current Name</td>

I know I can access a field's value via form.name.data, but is there any way I can keep the same template with form.name() being called and somehow toggle whether that outputs <input type="text"... or Current Name?

like image 924
Sarah Vessels Avatar asked Jun 23 '11 15:06

Sarah Vessels


3 Answers

I created a custom widget:

from wtforms.fields import Field

class PlainTextWidget(object):
    def __call__(self, field, **kwargs):
        return field.data if field.data else ''

Then, for my view page, I added the following:

form = MyForm(obj=myDataRow)
fields = [val for val in form._fields]
for fieldName in fields:
    fieldProp = getattr(form, fieldName)
    setattr(fieldProp, 'widget', PlainTextWidget())
like image 194
Sarah Vessels Avatar answered Oct 14 '22 20:10

Sarah Vessels


Sarah's answer above led me to the solution to a related problem: What if you want some of your fields to be read only? In that case, instead of doing run-time surgery on the form object, you could define a new ROTextField variant (for example), that always renders to the pure value. For example:

from wtforms.widgets import Input
from wtforms.fields import StringField

class TextOutput(Input):
    def __call__(self, field, **kwargs):
        return kwargs.get('value', field._value())

class ROTextField(StringField):
    widget = TextOutput()

Now define your field with ReadOnly attributes:

class UserPrefs(Form):
    name     = ROTextField('name', default='Jon')
    # ...

Thinking about this problem helped me better understand how WTForms work. Leaving this here in case this might help someone else work through related issues.

like image 35
Jonathan Eunice Avatar answered Oct 14 '22 19:10

Jonathan Eunice


Based on Sarah's answer and code found in WTForms-Components I use the following to quickly turn all a form's fields into read-only and disabled fields.

Suppose we have a ProfileForm defined as follows:

class ProfileEditForm(Form):
    title = StringField("Title", validators=[validators.required("Please enter your title.")])
    first_name = StringField("First Name", validators=[validators.required("Please enter your first name.")])
    middle_name = StringField("Middle Name")
    last_name = StringField("Last Name", validators=[validators.required("Please enter your last name.")])
    organisation = StringField("Company Name", validators=[validators.required("Please enter your company name.")])
    organisation_website = StringField("Company Website")
    # more fields ...

Define the following class (based on ReadOnlyWidgetProxy from WTForms-Components):

class ReadOnlyAndDisabledWidgetProxy(object):
    def __init__(self, widget):
        self.widget = widget

    def __getattr__(self, name):
        return getattr(self.widget, name)

    def __call__(self, field, **kwargs):
        kwargs.setdefault('readonly', True)
        kwargs.setdefault('disabled', True)
        return self.widget(field, **kwargs)

Now inherit from ProfileForm as follows:

class ReadOnlyProfileForm(ProfileForm):
    def __init__(self, *args, **kwargs):
        super(ReadOnlyProfileForm, self).__init__(*args, **kwargs)
        for field_name in self._fields:
            field_property = getattr(self, field_name)
            field_property.widget = ReadOnlyAndDisabledWidgetProxy(field_property.widget)
like image 22
pjcunningham Avatar answered Oct 14 '22 21:10

pjcunningham