A Python HTML form library.

Overview

Deform

https://github.com/Pylons/deform/workflows/Build%20and%20test/badge.svg?branch=main Master Documentation Status Latest Documentation Status

Introduction

Deform is a Python form library for generating HTML forms on the server side. Date and time picking widgets, rich text editors, forms with dynamically added and removed items and a few other complex use cases are supported out of the box.

Deform integrates with the Pyramid web framework and several other web frameworks. Deform comes with Chameleon templates and Bootstrap 3 styling. Under the hood, Colander schemas are used for serialization and validation. The Peppercorn library maps HTTP form submissions to nested structure.

Although Deform uses Chameleon templates internally, you can embed rendered Deform forms into any template language.

Use cases

Deform is ideal for complex server-side generated forms. Potential use cases include:

  • Complex data entry forms
  • Administrative interfaces
  • Python based websites with high amount of data manipulation forms
  • Websites where additional front end framework is not needed

Installation

Install using pip and Python package installation best practices:

pip install deform

Example

See all widget examples. Below is a sample form loop using the Pyramid web framework.

https://github.com/Pylons/deform/raw/main/docs/example.png

Example code:

"""Self-contained Deform demo example."""
from __future__ import print_function

from pyramid.config import Configurator
from pyramid.session import UnencryptedCookieSessionFactoryConfig
from pyramid.httpexceptions import HTTPFound

import colander
import deform


class ExampleSchema(deform.schema.CSRFSchema):

    name = colander.SchemaNode(
        colander.String(),
        title="Name")

    age = colander.SchemaNode(
        colander.Int(),
        default=18,
        title="Age",
        description="Your age in years")


def mini_example(request):
    """Sample Deform form with validation."""

    schema = ExampleSchema().bind(request=request)

    # Create a styled button with some extra Bootstrap 3 CSS classes
    process_btn = deform.form.Button(name='process', title="Process")
    form = deform.form.Form(schema, buttons=(process_btn,))

    # User submitted this form
    if request.method == "POST":
        if 'process' in request.POST:

            try:
                appstruct = form.validate(request.POST.items())

                # Save form data from appstruct
                print("Your name:", appstruct["name"])
                print("Your age:", appstruct["age"])

                # Thank user and take him/her to the next page
                request.session.flash('Thank you for the submission.')

                # Redirect to the page shows after succesful form submission
                return HTTPFound("/")

            except deform.exception.ValidationFailure as e:
                # Render a form version where errors are visible next to the fields,
                # and the submitted values are posted back
                rendered_form = e.render()
    else:
        # Render a form with initial default values
        rendered_form = form.render()

    return {
        # This is just rendered HTML in a string
        # and can be embedded in any template language
        "rendered_form": rendered_form,
    }


def main(global_config, **settings):
    """pserve entry point"""
    session_factory = UnencryptedCookieSessionFactoryConfig('seekrit!')
    config = Configurator(settings=settings, session_factory=session_factory)
    config.include('pyramid_chameleon')
    deform.renderer.configure_zpt_renderer()
    config.add_static_view('static_deform', 'deform:static')
    config.add_route('mini_example', path='/')
    config.add_view(mini_example, route_name="mini_example", renderer="templates/mini.pt")
    return config.make_wsgi_app()

This example is in deformdemo repository. Run the example with pserve:

pserve mini.ini --reload

Status

This library is actively developed and maintained. Deform 2.x branch has been used in production on several sites since 2014. Automatic test suite has 100% Python code coverage and 500+ tests.

Projects using Deform

Community and links

Comments
  • Chicken-and-egg problem between repos of Deform and deformdemo

    Chicken-and-egg problem between repos of Deform and deformdemo

    Would it solve the chicken and egg problem, making it possible for us to test simultaneous changes in both Deform and deformdemo in GitHub Actions or Travis, without specifying from where to pull the changes?

    Not by itself, containers just make running the tests more easily reproducible across different systems. The core problem I think is that deform and deformdemo are by there nature tightly coupled, so their releases, PR's, features etc should be completely in sync. The best way I know how to do that is a monorepo; i.e. merging deformdemo into deform. I would understand if that is not an option, but I think that is fundamentally the easiest solution.

    But to keep the current split, a few options crossed my mind:

    1. create a "next" branch on both repositories that is used in the tests. The next branch is either equal to master, or has some extra's bolted on to progress to the next step. In the deform repo a PR to master is tested using the next branch of deformdemo. A PR for deformdemo@master is tested using deform@next. So a new feature (that influences both repo's) requires PR's to be made to both next branches. These PR's are merged without testing, and then new PR's are made for merging next into master. This all feels quite cumbersome, especially when iterations are needed to get stuff ready. Also this doesn't allow multiple PR's for different features to co-exist. Effectively what I would want is that when I make a PR into Pylons/deform@master from tdamsma/deform@, the tests are run using tdamsma/deformdemo@<some-new-feature and not Pylons/deformdemo@master. Probably technically possible to set up, but it sounds way to convoluted

    2. use git submodules and tests against whatever the current commit of the submodule is. I am not so experienced with this so not really sure if this would actually work

    3. require a certain order for changes: first commit/merge a PR in deformdemo, and only the make a PR for deform. Use some kind of decorator to skip tests for unavailable features depending on the deform version, something like pytest.mark.skipif(deform.__version__ <= "3.11")

    To be honest I don't like any of these options much. Perhaps this should be a separate discussion for the containerization?

    Originally posted by @tdamsma in https://github.com/Pylons/deform/issues/471#issuecomment-685113416

    opened by stevepiercy 34
  • Form focus

    Form focus

    Further to #244, #249 and #250

    Current behaviour

    When a page is loaded, the first input of the first form is automatically focused.

    Problematic behaviour

    If the first form is below the fold, web browsers automatically scroll down to the focused element. This means that, in some cases, a user may load a page and immediately be sent down to wherever the form is. This issue is particularly apparent on mobile/responsive pages.

    Proposed solution

    Forms are to have an additional argument to determine if input focusing should be done automatically, manually or not at all. In the case of manual focusing, child input fields of the form will have an additional argument to determine if they are to receive focus.

    Forms can take an additional argument when they are created. focus_form = 'auto' | 'manual' | 'off' This results in the form being rendered with a HTML attribute data-deform-focus-form with any of the above values.

    The javascript function focusFirstInput will perform the following.

    | focus_form | action | | --- | --- | | 'auto' | First input of the first form is focused | | 'manual' | First input with manual_focus set is focused | | 'off' | No focusing is done | | omitted | Same as 'auto' |

    Examples

    Default

    class SchemaInputDefault(colander.Schema):
        name = 'Default Input Focus'
        input = colander.SchemaNode(
        colander.String(),
        title = 'Input',
        missing = '')
    
    class TestForm():
        def __init__(self, request):
            schema = SchemaInputDefault(validator=self.validate).bind(request=request)
            self.form = deform.Form(schema, buttons=(button,))
    

    Auto

    class SchemaInputAuto(colander.Schema):
        name = 'Auto Input Focus'
        input = colander.SchemaNode(
                      colander.String(),
                      title = 'Input',
                      missing = '')
    
    class TestForm():
        def __init__(self, request):
            schema = SchemaInputAuto(validator=self.validate).bind(request=request)
            self.form = deform.Form(schema, buttons=(button,), focus_form='auto')
    

    Manual

    class SchemaInputManual(colander.Schema):
        name = 'Manual Input Focus'
    
        input1 = colander.SchemaNode(
                        colander.String(),
                        title = 'Input 1',
                        missing = '')
    
        input2 = colander.SchemaNode(
                        colander.String(),
                        title = 'Input 2 (Will be focused)',
                        missing = '',
                        manual_focus = 'on')
    
        input3 = colander.SchemaNode(
                        colander.String(),
                        title = 'Input 3',
                        missing = '')
    
    class TestForm():
        def __init__(self, request):
            schema = SchemaInputManual(validator=self.validate).bind(request=request)
            self.form = deform.Form(schema, buttons=(button,), focus_form='manual')
    

    Off

    class SchemaInputOff(colander.Schema):
        name = 'No Input Focus'
        input = colander.SchemaNode(
                     colander.String(),
                     title = 'Input',
                     missing = '')
    
    class TestForm():
        def __init__(self, request):
            schema = SchemaInputOff(validator=self.validate).bind(request=request)
            self.form = deform.Form(schema, buttons=(button,), focus_form='off')
    

    Results

    • Default behaviour is unchanged
    • On page load, the first input field of the first form receives focus (default)
    • Any one input field can be chosen to receive focus
    • Automatic focusing can be disabled on a per-form basis

    Existing nose tests pass. Some basic tests were added (checking defaults etc). I'm not sure if this is a big enough change to warrant any extra examples on deformdemo, but I can put together a pull request for that if it might be worthwhile.

    opened by alephex 28
  • Add HTML5 attributes to text widgets.

    Add HTML5 attributes to text widgets.

    With this, textarea, textinput and password templates support the maxlength, placeholder and required attributes.

    • required already exists as a field attribute so we just read that.
    • maxlength and placeholder are read from the widget instance.

    Also, HTML5 supports several new type values ('date', 'number' etc.) so this becomes flexible in textinput.pt so this template can be reused. It reads widget.type and defaults to "text".

    What about people who aren't using HTML 5?

    Why, I don't know, are there any?

    Maybe it is better if you tell me how to proceed. Should Deform be globally informed about the HTML version, then templates can be adjusted? How would we do this?

    opened by nandoflorestan 23
  • Cancel button?

    Cancel button?

    Maybe I'm missing something obvious, but I don't see any way to add a simple "cancel" button to a form. Essentially a button that redirects the user to another page instead of submitting the content of the page.

    I've tried adding a button with the deform.form.Button class with type="button", but there's no way to give it any sort of functionality such as giving it an onclick value.

    enhancement sprintable 
    opened by tisdall 20
  • deform2: AutocompleteInputWidget - more powerful configuration for typeahead.js

    deform2: AutocompleteInputWidget - more powerful configuration for typeahead.js

    This adds a datasets parameter to AutocompleteInputWidget which can be used as an alternatives to the values parameter for configuring the typeahead.js plugin.

    Basically the value of datasets (if given) is JSONified and passed directly to the typeahead plugin. In addition, a custom JSONifier is used. The JSONifier respects a literal_js marker class that can be used to include hunks of literal javascript within the datasets parameter.

    (A patch to deformdemo with a demo is forthcoming.)

    opened by dairiki 19
  • Feature: Widget's css_class should default to field name

    Feature: Widget's css_class should default to field name

    Often times, we want to apply styles to certain form elements that can easily done purely in CSS. It feels like overkill to create a new widget for every schema node just to set the css class.

    It would be a lot more helpful if the css_class of deform's Widget would default to a daherized/sanitized version of field.name with "deform-" prepended.

    opened by rbu 18
  • Add traditional chinese localization

    Add traditional chinese localization

    Hi there,

    we got this translation from one of our users in Taiwan who complained about simplified Chinese characters in the app.

    Now I'm not speaking any Chinese so it is kind of hard for me to judge this (or wether you would want to take this in).

    However, I can offer some observations: It looks like zh (the base translation) already has traditional Chinese characters. This is, to my understanding, a bit odd, as zh (without script qualifier) should probably be simplified Chinese, as that is used by the vast majority of Chinese readers. (i.e. it should probably symlink to zh_Hans? Or maybe zh_Hans is just moved to zh_Hant?)

    For us the most important part is that a request for zh_Hant actually returns traditional chinese characters (which it seems to not have in the past) -> which is why we provide this pull request.

    enhancement 
    opened by dwt 14
  • read-only fields on validation error

    read-only fields on validation error

    Read-only fields are only displayed and aren't included on form submission. If a person submits a form with read-only elements those elements aren't included. If the form doesn't validate, then, deform.exception.ValidationFailure.render() doesn't contain the previous values for those read-only elements and then leaves it blank.

    What's the typical way of handling this issue? It seems like it's not dealt with at http://deform2demo.repoze.org/readonly_argument/ (just try submitting the page there and all the values disappear)

    Should we be including hidden fields with read-only elements purely for display usage?

    enhancement needs-pr should backport 
    opened by tisdall 14
  • make dateinput work again by using the fixed name

    make dateinput work again by using the fixed name "date" as expected by ...

    ...the pstruct schema

    I don't really understand why the date input was changed from being a simple single input to a mapping, but this change to the template makes it so it actually works. Without this change you get the following error message: "Invalid pstruct: {'date': 'Required'}" (unless, of course, if you happened to name your dateinput field "date")

    opened by tisdall 14
  • deform2: fix up file_upload.pt

    deform2: fix up file_upload.pt

    This makes the FileUploadWidget look a bit nicer (and more legible).

    More work is probably in order on this front. I believe javscript is necessary to make the file upload control match the other form controls. A quick search has found two projects which might be useful, though neither looks necessarily ideal:

    • Jasny’s fork of bootstrap 2. Code here, demo here.
    • Also found this http://gregpike.net/demos/bootstrap-file-input/demo.html
    • And this http://markusslima.github.io/bootstrap-filestyle/

    Are there better choices?

    opened by dairiki 13
  • Add the ``deform.widget.DateTimeWidget`` widget, which uses the jQueryUI Timepicker add-on.

    Add the ``deform.widget.DateTimeWidget`` widget, which uses the jQueryUI Timepicker add-on.

    DateTimeWidget uses the ISO8601 combined date and time format internally, as expected by colander.DateTime, but converts to the more friendly separate date and time format for display in the widget.

    opened by dnouri 13
  • Show preview of image in FileUploadWidget?

    Show preview of image in FileUploadWidget?

    I'm a newbie in programming and can't find any helpful documentation for my problem... I want to use deform in my pyramid app. I used the FileUploadWidget to save my images in db. This works fine, but i can't figured out how I can show a preview of the uploaded image (eg in edit form).

    Can you help me, or give me a hint for my problem?

    thx :)

    opened by ITChick2018 4
  • Fix bug when CSRF token does not appear in a form

    Fix bug when CSRF token does not appear in a form

    This fixes a bug when csrf_token value appears empty in a form in the case when a schema has been created before creating a session factory, i.e. ..input type="hidden" name="csrf_token" value="" id=.... Also, calling the function deferred_csrf_value() directly produced an error message "No session factory registered (see the Sessions chapter of the Pyramid documentation)". My fix calls the safe function get_csrf_token() which always returns a token, creates it if neccessary.

    opened by adisloom 2
  • deserialize lost data

    deserialize lost data

    Hi! This is my form post: MultiDict([('_charset_', 'UTF-8'), ('__formid__', 'deform'), ('token', '2993df2af005fdedea14ea7d79dcde97c7b0e677'), ('__start__', 'sdate:mapping'), ('date', '2021-08-20'), ('dt_date-visible', '20/08/2021'), ('__end__', 'sdate:mapping'), ('__start__', 'estimated_edate:mapping'), ('date', '2021-08-20'), ('dt_date-visible', '20/08/2021'), ('__end__', 'estimated_edate:mapping'), ('name', 'AAA'), ('description', 'DDD'), ('metric', '364'), ('__start__', 'nodes:sequence'), ('nodes', '116'), ('__end__', 'nodes:sequence'), ('__start__', 'clusters:sequence'), ('clusters', '140'), ('__end__', 'clusters:sequence'), ('__start__', 'users:mapping'), ('expert', 'NAVARRO, Teresa (id: NAT)'), ('__start__', 'groups:sequence'), ('__end__', 'groups:sequence'), ('__start__', 'assistants:sequence'), ('__end__', 'assistants:sequence'), ('send_message', 'true'), ('__end__', 'users:mapping'), ('__start__', 'transactions:sequence'), ('__start__', 'transaction:mapping'), ('id', ''), ('id_transaction', '321'), ('__start__', 'recording:sequence'), ('__start__', 'recording:mapping'), ('uid', 'IHWHBX5BDQ'), ('s3_filename', 'logo-Telegram.png'), ('s3_final_url', 'https://bucket.s3.amazonaws.com/45f57055-de02-4136-9122-7b650eadbfd4'), ('s3_mimetype', 'image/png'), ('s3_upload_finished', '1'), ('s3_upload_attempts', '1'), ('__end__', 'recording:mapping'), ('__end__', 'recording:sequence'), ('__end__', 'transaction:mapping'), ('__end__', 'transactions:sequence'), ('Create', 'Crear')]) When there is a form error, I found this in my pstruct:

    env/lib/python3.6/site-packages/deform/field.py(786)validate_pstruct()
        785         import ipdb;ipdb.set_trace()
    --> 786         try:
        787             cstruct = self.deserialize(pstruct)
    

    ipdb> pstruct {'_charset_': 'UTF-8', '__formid__': 'deform', 'token': '2993df2af005fdedea14ea7d79dcde97c7b0e677', 'sdate': {'date': '2021-08-20', 'dt_date-visible': '20/08/2021'}, 'estimated_edate': {'date': '2021-08-20', 'dt_date-visible': '20/08/2021'}, 'name': 'AAA', 'description': 'DDD', 'metric': '364', 'nodes': ['116'], 'clusters': ['140'], 'users': {'expert': 'NAVARRO, Teresa (id: NAT)', 'groups': [], 'assistants': [], 'send_message': 'true'}, 'transactions': [{'id': '', 'id_transaction': '321', 'recording': [{'uid': 'IHWHBX5BDQ', 's3_filename': 'logo-Telegram.png', 's3_final_url': 'https://bucket.s3.amazonaws.com/45f57055-de02-4136-9122-7b650eadbfd4', 's3_mimetype': 'image/png', 's3_upload_finished': '1', 's3_upload_attempts': '1'}]}], 'Create': 'Crear'}

    ipdb> self.deserialize(pstruct) <-- here the deserialization {'token': '2993df2af005fdedea14ea7d79dcde97c7b0e677', 'sdate': '2021-08-20', 'estimated_edate': '2021-08-20', 'name': 'AAA', 'description': 'DDD', 'metric': '364', 'nodes': ('116',), 'clusters': ('140',), 'users': {'expert': 'NAVARRO, Teresa (id: NAT)', 'groups': [], 'assistants': [], 'send_message': 'true'}, 'transactions': [{'id': <colander.null>, 'id_transaction': '321', 'recording': [{'filename': '', 'uid': 'IHWHBX5BDQ', 'mimetype': None, 'size': None, 'fp': None, 'preview_url': None}], 'performance': 'false'}]} ipdb>

    when the form return the error, the recording key inside the transactions key, lost the values. I resolve it doing somethig like this: cstruct['transactions'] = pstruct['transactions']

    Now, the new cstruct is what i need: {'token': '2993df2af005fdedea14ea7d79dcde97c7b0e677', 'sdate': '2021-08-20', 'estimated_edate': '2021-08-20', 'name': 'AAA', 'description': 'DDD', 'metric': '364', 'nodes': ('116',), 'clusters': ('140',), 'users': {'expert': 'NAVARRO, Teresa (id: NAT)', 'groups': [], 'assistants': [], 'send_message': 'true'}, 'transactions': [{'id': '', 'id_transaction': '321', 'recording': [{'uid': 'IHWHBX5BDQ', 's3_filename': 'logo-Telegram.png', 's3_final_url': 'https://bucket.s3.amazonaws.com/45f57055-de02-4136-9122-7b650eadbfd4', 's3_mimetype': 'image/png', 's3_upload_finished': '1', 's3_upload_attempts': '1'}]}]} and when this line runing: if exc: raise exception.ValidationFailure(self, cstruct, exc) The form tell me the error and no data is lost

    Can it will be an issue to fix? or i have an error in my form post structure??

    opened by apocalipsys 3
  • Support for Content-Security-Policy headers via nonce attribute of script/style tags

    Support for Content-Security-Policy headers via nonce attribute of script/style tags

    Suggested fix for https://github.com/Pylons/deform/issues/512.

    (Code style uses Python 3 type hinting, since setup.py has Python 3.6 as the minimum version.)

    opened by RudolfCardinal 0
Releases(2.0.15)
Owner
Pylons Project
The Pylons Project is composed of a disparate group of project leaders with experience going back to the very start of Python web frameworks.
Pylons Project
Easy and free contact form on your HTML page. No backend or JS required.

Easy and free contact form on your HTML page. No backend or JS required. ?? ??

0xDEADF00D 8 Dec 16, 2022
A platform independent django form serializer

django-remote-forms A package that allows you to serialize django forms, including fields and widgets into Python dictionary for easy conversion into

WiserTogether, Inc. 219 Sep 20, 2022
Full control of form rendering in the templates.

django-floppyforms Full control of form rendering in the templates. Authors: Gregor Müllegger and many many contributors Original creator: Bruno Renié

Jazzband 811 Dec 1, 2022
Automate your google form here!

Google Form Filler (GFF) - Automate your google form here! About The idea of this project came from my online lectures as one of my professors takes a

Jay Thorat 13 Jan 5, 2023
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

null 4.6k Jan 5, 2023
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

null 4.6k Dec 31, 2022
A flexible forms validation and rendering library for Python.

WTForms WTForms is a flexible forms validation and rendering library for Python web development. It can work with whatever web framework and template

WTForms 1.4k Dec 31, 2022
FlaskBB is a Forum Software written in Python using the micro framework Flask.

FlaskBB is a Forum Software written in Python using the micro framework Flask.

FlaskBB 2.3k Dec 30, 2022
Tweak the form field rendering in templates, not in python-level form definitions. CSS classes and HTML attributes can be altered.

django-widget-tweaks Tweak the form field rendering in templates, not in python-level form definitions. Altering CSS classes and HTML attributes is su

Jazzband 1.8k Jan 6, 2023
A Python HTML form library.

Deform Introduction Use cases Installation Example Status Projects using Deform Community and links Introduction Deform is a Python form library for g

Pylons Project 391 Jan 3, 2023
GMailBomber is a form of Internet abuse which is perpetrated through the sending of massive volumes of email to a specific email address with the goal of overflowing the mailbox and overwhelming the mail server hosting the address, making it into some form of denial of service attack.

GMailBomber is a form of Internet abuse which is perpetrated through the sending of massive volumes of email to a specific email address with the goal of overflowing the mailbox and overwhelming the mail server hosting the address, making it into some form of denial of service attack.

Muneeb 5 Nov 13, 2022
Resolve form field arguments dynamically when a form is instantiated

django-forms-dynamic Resolve form field arguments dynamically when a form is instantiated, not when it's declared. Tested against Django 2.2, 3.2 and

DabApps 108 Jan 3, 2023
Easy HTML form without PHP or JavaScript

This repository is no longer active. If you're looking for a simple and powerful hosted form API, please check out https://formspree.io. If you are in

Formspree 2.8k Dec 22, 2022
Easy and free contact form on your HTML page. No backend or JS required.

Easy and free contact form on your HTML page. No backend or JS required. ?? ??

0xDEADF00D 8 Dec 16, 2022
A HTML-code compiler-thing that lets you reuse HTML code.

RHTML RHTML stands for Reusable-Hyper-Text-Markup-Language, and is pronounced "Rech-tee-em-el" despite how its abbreviation is. As the name stands, RH

Duckie 4 Nov 15, 2021
Use minify-html, the extremely fast HTML + JS + CSS minifier, with Django.

django-minify-html Use minify-html, the extremely fast HTML + JS + CSS minifier, with Django. Requirements Python 3.8 to 3.10 supported. Django 2.2 to

Adam Johnson 60 Dec 28, 2022
That project takes as input special TXT File, divides its content into lsit of HTML objects and then creates HTML file from them.

That project takes as input special TXT File, divides its content into lsit of HTML objects and then creates HTML file from them.

null 1 Jan 10, 2022
Lektor-html-pretify - Lektor plugin to pretify the HTML DOM using Beautiful Soup

html-pretify Lektor plugin to pretify the HTML DOM using Beautiful Soup. How doe

Chaos Bodensee 2 Nov 8, 2022
Django-Text-to-HTML-converter - The simple Text to HTML Converter using Django framework

Django-Text-to-HTML-converter This is the simple Text to HTML Converter using Dj

Nikit Singh Kanyal 6 Oct 9, 2022
A collection of existing KGQA datasets in the form of the huggingface datasets library, aiming to provide an easy-to-use access to them.

KGQA Datasets Brief Introduction This repository is a collection of existing KGQA datasets in the form of the huggingface datasets library, aiming to

Semantic Systems research group 21 Jan 6, 2023