def update_actions(self): """ Prepare form actions, this method should be called directly. ``Form.update`` calls this method during initialization.""" self.actions = Actions(self, self.request) self.actions.update()
class Form(object): """ A form ``id``: Form id ``name``: Form name ``label``: Form label ``description``: Form description ``prefix``: Form prefix, it used for html elements `id` generations. ``fields``: Form fields :py:class:`pform.Fieldset` ``buttons``: Form buttons :py:class:`pform.Buttons` ``actions``: Instance of :py:class:`pform.Actions` class ``widgets``: Instance of :py:class:`FormWidgets` class ``content``: Form content, it should be `None` or dictionary with data for fields. ``params``: Form request parameters ``action``: Form action, by default ``request.url`` ``method``: HTML Form method (`post`, `get`) ``csrf``: Enable/disable form csrf protection ``csrf_name``: Form csrf field name ``csrf_token``: Form csrf token value """ label = None description = '' prefix = 'form.' actions = None widgets = None buttons = None fields = Fieldset() content = None method = 'post' enctype = 'multipart/form-data' accept = None accept_charset = 'utf-8' params = None context = None klass = 'form-horizontal' csrf = False csrf_name = 'csrf-token' csrf_token = '' tmpl_view = 'form:form' tmpl_actions = 'form:form-actions' tmpl_widget = 'form:widget' __name__ = '' __parent__ = None __view_mapper__ = FormViewMapper def __init__(self, context, request, **kw): self.__dict__.update(kw) self.context = context self.request = request self.__parent__ = context if self.buttons is None: self.buttons = Buttons() # convert fields to Fieldset if not isinstance(self.fields, Fieldset): self.fields = Fieldset(*self.fields) # set tmpl_widget for fieldset in self.fields.fieldsets(): for field in fieldset.fields(): if field.cls.tmpl_widget is None: field.cls.tmpl_widget = self.tmpl_widget @reify def id(self): return self.name.replace('.', '-') @reify def name(self): return self.prefix.strip('.') @reify def action(self): return self.request.url @reify def csrf_token(self): return self.request.session.get_csrf_token() def form_content(self): """ Return form content. By default it returns ``Form.content`` attribute. """ return self.content def form_params(self): """ get form request params """ if self.params is not None: if not isinstance(self.params, MultiDict): return MultiDict(self.params) return self.params if self.method == 'post': return self.request.POST elif self.method == 'get': return self.request.GET else: return self.params def update_widgets(self): """ prepare form widgets """ self.widgets = FormWidgets(self.fields, self, self.request) self.widgets.update() def update_actions(self): """ Prepare form actions, this method should be called directly. ``Form.update`` calls this method during initialization.""" self.actions = Actions(self, self.request) self.actions.update() def update_form(self, data=None): """ update form """ if not self.content and data: self.content = data self.update_widgets() self.update_actions() ac_result = self.actions.execute() if IResponse.providedBy(ac_result): raise HTTPResponseIsReady(ac_result) result = self.update() if IResponse.providedBy(result): raise HTTPResponseIsReady(result) if result is None: result = {} if ac_result is not None: result.update(ac_result) return result def update(self): """ Update form """ return {} def render(self): """ render form """ return render(self.request, self.tmpl_view, self, actions = self.actions, widgets = self.widgets) def validate(self, data, errors): """ Custom form validation """ def validate_form(self, data, errors): """ Form validation """ self.validate_csrf_token() try: self.validate(data, errors) except Invalid as err: errors.append(err) def validate_csrf_token(self): """ csrf token validation """ if self.csrf: token = self.form_params().get(self.csrf_name, None) if token is not None: if self.csrf_token == token: return raise HTTPForbidden("Form authenticator is not found.") def extract(self): """ extract form values """ return self.widgets.extract() def add_error_message(self, msg): """ add form error message """ add_message(self.request, msg, 'form:error') def __call__(self): """ update form and render form to response """ try: result = self.update_form() except HTTPResponseIsReady as result: return result.args[0] except HTTPException as result: return result response = self.request.registry.queryAdapterOrSelf(result, IResponse) if response is not None: return response body = self.render() response = self.request.response if isinstance(body, bytes): response.body = body else: response.text = body return response