tbxforms


Nametbxforms JSON
Version 1.1.0 PyPI version JSON
download
home_pagehttps://github.com/torchbox/tbxforms/
SummaryA Torchbox-flavoured template pack for django-crispy-forms, adapted from crispy-forms-gds
upload_time2023-08-16 13:04:01
maintainer
docs_urlNone
authorKyle Bayliss
requires_python>=3.8.1,<4.0
licenseBSD-2-Clause
keywords crispy django django crispy forms django crispy form forms gds tbxforms wagtail wagtail forms wagtail form
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![PyPI](https://img.shields.io/pypi/v/tbxforms.svg)](https://pypi.org/project/tbxforms/)
[![npm](https://img.shields.io/npm/v/tbxforms.svg)](https://www.npmjs.com/package/tbxforms) [![PyPI downloads](https://img.shields.io/pypi/dm/tbxforms.svg)](https://pypi.org/project/tbxforms/) [![CI](https://github.com/torchbox/tbxforms/actions/workflows/test.yml/badge.svg)](https://github.com/torchbox/tbxforms/actions/workflows/test.yml) [![LGTM](https://img.shields.io/lgtm/alerts/g/torchbox/tbxforms.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/torchbox/tbxforms/alerts/)

# Torchbox Forms

A Torchbox-flavoured template pack for [django-crispy-forms](https://github.com/django-crispy-forms/django-crispy-forms), adapted from [crispy-forms-gds](https://github.com/wildfish/crispy-forms-gds).

Out of the box, forms created with `tbxforms` will look like the
[GOV.UK Design System](https://design-system.service.gov.uk/), though many
variables can be customised.

## Requirements

-   Python `>=3.8.1,<4.0`
-   Django `>=2.2,<=4.0`
-   Wagtail `>=2.15` (if using `WagtailBaseForm`)

## Installation

You must install both the Python package and the NPM package.

### Install the Python package

Install using pip:

```bash
pip install tbxforms
```

Add `django-crispy-forms` and `tbxforms` to your installed apps:

```python
INSTALLED_APPS = [
  # ...
  'crispy_forms',  # django-crispy-forms
  'tbxforms',
]
```

Now add the following settings to tell `django-crispy-forms` to use `tbxforms`:

```python
CRISPY_ALLOWED_TEMPLATE_PACKS = ["tbx"]
CRISPY_TEMPLATE_PACK = "tbx"
```

### Install the NPM package

Install using NPM:

```bash
npm install tbxforms
```

Note: This package uses the `Element.closest`, `NodeList.forEach`, and
`Array.includes` APIs. You will need to install and configure polyfills for
legacy browser support if you need to.

Instantiate your forms:

```javascript
import TbxForms from 'tbxforms';

document.addEventListener('DOMContentLoaded', () => {
    for (const form of document.querySelectorAll(TbxForms.selector())) {
        new TbxForms(form);
    }
});
```

Import the styles into your project...

...Either as CSS without any customisations:

```scss
@use 'node_modules/tbxforms/style.css';
```

...Or as Sass to customise variables:

```scss
@use 'node_modules/tbxforms/tbxforms.scss' with (
    $tbxforms-error-colour: #f00,
    $tbxforms-text-colour: #000,
);
```

Alternatively, variables can be defined in a centralised variables SCSS
such as [tbxforms/static/sass/abstracts/\_variables.scss](https://github.com/torchbox/tbxforms/blob/main/tbxforms/static/sass/abstracts/_variables.scss).

#### Add button styles

`tbxforms` provides out-of-the-box GOV.UK Design System styles for everything
except buttons, as styles for these probably exist within your project.

You will need to write button styles for the following classes:

1. `.tbxforms-button`
2. `.tbxforms-button.tbxforms-button--primary`
3. `.tbxforms-button.tbxforms-button--secondary`
4. `.tbxforms-button.tbxforms-button--warning`

## Usage

`tbxforms` can be used for coded Django forms and editor-controlled Wagtail forms.

### Django forms

All forms must inherit the `TbxFormsMixin` mixin, as well as specifying a Django base form class (e.g. `forms.Form` or `forms.ModelForm`)

```python
from django import forms
from tbxforms.forms import TbxFormsMixin

class ExampleForm(TbxFormsMixin, forms.Form):
    ...

class ExampleModelForm(TbxFormsMixin, forms.ModelForm):
    ...
```

### Wagtail forms

#### Create or update a Wagtail form

Wagtail forms must inherit from `TbxFormsMixin` and `WagtailBaseForm`.

```python
from wagtail.contrib.forms.forms import BaseForm as WagtailBaseForm
from tbxforms.forms import TbxFormsMixin

class ExampleWagtailForm(TbxFormsMixin, WagtailBaseForm):
    ...
```

#### Instruct a Wagtail Page model to use your form

**In your form definitions** (e.g. forms.py):

```python
from tbxforms.forms import BaseWagtailFormBuilder as TbxFormsBaseWagtailFormBuilder
from path.to.your.forms import ExampleWagtailForm

class WagtailFormBuilder(TbxFormsBaseWagtailFormBuilder):
    def get_form_class(self):
        return type(str("WagtailForm"), (ExampleWagtailForm,), self.formfields)
```

**And in your form page models** (e.g. models.py):

```python
from path.to.your.forms import WagtailFormBuilder

class ExampleFormPage(...):
    ...
    form_builder = WagtailFormBuilder
    ...
```

### Render a form

Just like Django Crispy Forms, you need to pass your form object to the
`{% crispy ... %}` template tag, e.g.:

```html
{% load crispy_forms_tags %}

<html>
    <body>
        {% crispy your_sexy_form %}
    </body>
</html>
```

### `FormHelper`s

A [FormHelper](https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html)
allows you to alter the rendering behaviour of forms.

Every form that inherits from `TbxFormsMixin` (i.e. every form within `tbxforms`)
will have a `FormHelper` with the following default attributes:

-   `html5_required = True`
-   `label_size = Size.MEDIUM`
-   `legend_size = Size.MEDIUM`
-   `form_error_title = _("There is a problem with your submission")`
-   Plus everything from [django-crispy-forms' default attributes](https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html).

These can be changed during instantiation or [on the go](https://django-crispy-forms.readthedocs.io/en/latest/dynamic_layouts.html) - examples below.

#### Add a submit button

Submit buttons are not automatically added to forms. To add one, you can extend
the `form.helper.layout` (examples below).

Extend during instantiation:

```python
from django import forms
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Button

class YourSexyForm(TbxFormsMixin, forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper.layout.extend([
            Button.primary(
                name="submit",
                type="submit",
                value="Submit",
            )
        ])
```

Or afterwards:

```python
from tbxforms.layout import Button

form = YourSexyForm()
form.helper.layout.extend([
    Button.primary(
        name="submit",
        type="submit",
        value="Submit",
    )
])
```

### Conditionally-required fields

`tbxforms` can show/hide parts of the `layout` depending on a given value. For
example, you could show (and require) an email address field only when the user
chooses to sign up to a newsletter (examples below).

You can apply this logic to `field`, `div`, and `fieldset` elements.

Note: any field names included within the
`conditional_fields_to_show_as_required()` method will appear on the frontend
as required, though will technically be `required=False`.

**Field example:**

```python
from django import forms
from django.core.exceptions import ValidationError
from tbxforms.choices import Choice
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Field, Layout

class ExampleForm(TbxFormsMixin, forms.Form):
    NEWSLETTER_CHOICES = (
        Choice("yes", "Yes please", hint="Receive occasional email newsletters."),
        Choice("no", "No thanks"),
    )

    newsletter_signup = forms.ChoiceField(
        choices=NEWSLETTER_CHOICES
    )

    email = forms.EmailField(
        widget=forms.EmailInput(required=False)
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper.layout = Layout(
            # Add our newsletter sign-up field.
            Field("newsletter_signup"),

            # Add our email field and define the conditional logic.
            Field(
                "email",
                data_conditional={
                    "field_name": "newsletter_signup", # Field to inspect.
                    "values": ["yes"], # Value(s) to cause this field to show.
                },
            ),
        )

    @staticmethod
    def conditional_fields_to_show_as_required() -> [str]:
        # Non-required fields that should show as required to the user.
        return [
            "email",
        ]

    def clean(self):
        cleaned_data = super().clean()
        newsletter_signup = cleaned_data.get("newsletter_signup")
        email = cleaned_data.get("email")

        # Fields included within `conditional_fields_to_show_as_required()` will
        # be shown as required but not enforced - i.e. they will not have the
        # HTML5 `required` attribute set.
        # Thus we need to write our own check to enforce the value exists.
        if newsletter_signup == "yes" and not email:
            raise ValidationError(
                {
                    "email": "This field is required.",
                }
            )
        # The tbxforms JS will attempt to clear any redundant data upon submission,
        # though it is recommended to also handle this in your clean() method.
        elif newsletter_signup == "no" and email:
            del cleaned_data['email']

        return cleaned_data

```

**Container example:**

When you have multiple fields/elements that you want to show/hide together, you
can use the exact same `data_conditional` definition as above but on a `div` or
`fieldset` element, e.g.:

```python
from tbxforms.layout import HTML, Div, Field, Layout

Layout(
    Div(
        HTML("<p>Some relevant text.</p>"),
        Field("some_other_field"),
        Field("email"),
        data_conditional={
            "field_name": "newsletter_signup",
            "values": ["yes"],
        },
    ),
)
```

## Customising behaviour

### Allow HTML markup within labels and help text

Markup within labels and help text is disabled by default, though can be
enabled via:

```python
TBXFORMS_ALLOW_HTML_LABEL = False
TBXFORMS_ALLOW_HTML_HELP_TEXT = False
TBXFORMS_ALLOW_HTML_BUTTON = False
```

### Change the default label and legend classes

Possible values for the `label_size` and `legend_size`:

1. `SMALL`
2. `MEDIUM` (default)
3. `LARGE`
4. `EXTRA_LARGE`

# Further reading

-   Download the [PyPI package](http://pypi.python.org/pypi/tbxforms)
-   Download the [NPM package](https://www.npmjs.com/package/tbxforms)
-   Learn more about [Django Crispy Forms](https://django-crispy-forms.readthedocs.io/en/latest/)
-   Learn more about [Crispy Forms GDS](https://github.com/wildfish/crispy-forms-gds)
-   Learn more about [GOV.UK Design System](https://design-system.service.gov.uk/)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/torchbox/tbxforms/",
    "name": "tbxforms",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8.1,<4.0",
    "maintainer_email": "",
    "keywords": "crispy,django,django crispy forms,django crispy form,forms,gds,tbxforms,wagtail,wagtail forms,wagtail form",
    "author": "Kyle Bayliss",
    "author_email": "kyle.bayliss@torchbox.com",
    "download_url": "https://files.pythonhosted.org/packages/6b/0a/2100efc5c39238b1bdb00b3a3056122712049e6015e499f5633d17b5dadc/tbxforms-1.1.0.tar.gz",
    "platform": null,
    "description": "[![PyPI](https://img.shields.io/pypi/v/tbxforms.svg)](https://pypi.org/project/tbxforms/)\n[![npm](https://img.shields.io/npm/v/tbxforms.svg)](https://www.npmjs.com/package/tbxforms) [![PyPI downloads](https://img.shields.io/pypi/dm/tbxforms.svg)](https://pypi.org/project/tbxforms/) [![CI](https://github.com/torchbox/tbxforms/actions/workflows/test.yml/badge.svg)](https://github.com/torchbox/tbxforms/actions/workflows/test.yml) [![LGTM](https://img.shields.io/lgtm/alerts/g/torchbox/tbxforms.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/torchbox/tbxforms/alerts/)\n\n# Torchbox Forms\n\nA Torchbox-flavoured template pack for [django-crispy-forms](https://github.com/django-crispy-forms/django-crispy-forms), adapted from [crispy-forms-gds](https://github.com/wildfish/crispy-forms-gds).\n\nOut of the box, forms created with `tbxforms` will look like the\n[GOV.UK Design System](https://design-system.service.gov.uk/), though many\nvariables can be customised.\n\n## Requirements\n\n-   Python `>=3.8.1,<4.0`\n-   Django `>=2.2,<=4.0`\n-   Wagtail `>=2.15` (if using `WagtailBaseForm`)\n\n## Installation\n\nYou must install both the Python package and the NPM package.\n\n### Install the Python package\n\nInstall using pip:\n\n```bash\npip install tbxforms\n```\n\nAdd `django-crispy-forms` and `tbxforms` to your installed apps:\n\n```python\nINSTALLED_APPS = [\n  # ...\n  'crispy_forms',  # django-crispy-forms\n  'tbxforms',\n]\n```\n\nNow add the following settings to tell `django-crispy-forms` to use `tbxforms`:\n\n```python\nCRISPY_ALLOWED_TEMPLATE_PACKS = [\"tbx\"]\nCRISPY_TEMPLATE_PACK = \"tbx\"\n```\n\n### Install the NPM package\n\nInstall using NPM:\n\n```bash\nnpm install tbxforms\n```\n\nNote: This package uses the `Element.closest`, `NodeList.forEach`, and\n`Array.includes` APIs. You will need to install and configure polyfills for\nlegacy browser support if you need to.\n\nInstantiate your forms:\n\n```javascript\nimport TbxForms from 'tbxforms';\n\ndocument.addEventListener('DOMContentLoaded', () => {\n    for (const form of document.querySelectorAll(TbxForms.selector())) {\n        new TbxForms(form);\n    }\n});\n```\n\nImport the styles into your project...\n\n...Either as CSS without any customisations:\n\n```scss\n@use 'node_modules/tbxforms/style.css';\n```\n\n...Or as Sass to customise variables:\n\n```scss\n@use 'node_modules/tbxforms/tbxforms.scss' with (\n    $tbxforms-error-colour: #f00,\n    $tbxforms-text-colour: #000,\n);\n```\n\nAlternatively, variables can be defined in a centralised variables SCSS\nsuch as [tbxforms/static/sass/abstracts/\\_variables.scss](https://github.com/torchbox/tbxforms/blob/main/tbxforms/static/sass/abstracts/_variables.scss).\n\n#### Add button styles\n\n`tbxforms` provides out-of-the-box GOV.UK Design System styles for everything\nexcept buttons, as styles for these probably exist within your project.\n\nYou will need to write button styles for the following classes:\n\n1. `.tbxforms-button`\n2. `.tbxforms-button.tbxforms-button--primary`\n3. `.tbxforms-button.tbxforms-button--secondary`\n4. `.tbxforms-button.tbxforms-button--warning`\n\n## Usage\n\n`tbxforms` can be used for coded Django forms and editor-controlled Wagtail forms.\n\n### Django forms\n\nAll forms must inherit the `TbxFormsMixin` mixin, as well as specifying a Django base form class (e.g. `forms.Form` or `forms.ModelForm`)\n\n```python\nfrom django import forms\nfrom tbxforms.forms import TbxFormsMixin\n\nclass ExampleForm(TbxFormsMixin, forms.Form):\n    ...\n\nclass ExampleModelForm(TbxFormsMixin, forms.ModelForm):\n    ...\n```\n\n### Wagtail forms\n\n#### Create or update a Wagtail form\n\nWagtail forms must inherit from `TbxFormsMixin` and `WagtailBaseForm`.\n\n```python\nfrom wagtail.contrib.forms.forms import BaseForm as WagtailBaseForm\nfrom tbxforms.forms import TbxFormsMixin\n\nclass ExampleWagtailForm(TbxFormsMixin, WagtailBaseForm):\n    ...\n```\n\n#### Instruct a Wagtail Page model to use your form\n\n**In your form definitions** (e.g. forms.py):\n\n```python\nfrom tbxforms.forms import BaseWagtailFormBuilder as TbxFormsBaseWagtailFormBuilder\nfrom path.to.your.forms import ExampleWagtailForm\n\nclass WagtailFormBuilder(TbxFormsBaseWagtailFormBuilder):\n    def get_form_class(self):\n        return type(str(\"WagtailForm\"), (ExampleWagtailForm,), self.formfields)\n```\n\n**And in your form page models** (e.g. models.py):\n\n```python\nfrom path.to.your.forms import WagtailFormBuilder\n\nclass ExampleFormPage(...):\n    ...\n    form_builder = WagtailFormBuilder\n    ...\n```\n\n### Render a form\n\nJust like Django Crispy Forms, you need to pass your form object to the\n`{% crispy ... %}` template tag, e.g.:\n\n```html\n{% load crispy_forms_tags %}\n\n<html>\n    <body>\n        {% crispy your_sexy_form %}\n    </body>\n</html>\n```\n\n### `FormHelper`s\n\nA [FormHelper](https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html)\nallows you to alter the rendering behaviour of forms.\n\nEvery form that inherits from `TbxFormsMixin` (i.e. every form within `tbxforms`)\nwill have a `FormHelper` with the following default attributes:\n\n-   `html5_required = True`\n-   `label_size = Size.MEDIUM`\n-   `legend_size = Size.MEDIUM`\n-   `form_error_title = _(\"There is a problem with your submission\")`\n-   Plus everything from [django-crispy-forms' default attributes](https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html).\n\nThese can be changed during instantiation or [on the go](https://django-crispy-forms.readthedocs.io/en/latest/dynamic_layouts.html) - examples below.\n\n#### Add a submit button\n\nSubmit buttons are not automatically added to forms. To add one, you can extend\nthe `form.helper.layout` (examples below).\n\nExtend during instantiation:\n\n```python\nfrom django import forms\nfrom tbxforms.forms import TbxFormsMixin\nfrom tbxforms.layout import Button\n\nclass YourSexyForm(TbxFormsMixin, forms.Form):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper.layout.extend([\n            Button.primary(\n                name=\"submit\",\n                type=\"submit\",\n                value=\"Submit\",\n            )\n        ])\n```\n\nOr afterwards:\n\n```python\nfrom tbxforms.layout import Button\n\nform = YourSexyForm()\nform.helper.layout.extend([\n    Button.primary(\n        name=\"submit\",\n        type=\"submit\",\n        value=\"Submit\",\n    )\n])\n```\n\n### Conditionally-required fields\n\n`tbxforms` can show/hide parts of the `layout` depending on a given value. For\nexample, you could show (and require) an email address field only when the user\nchooses to sign up to a newsletter (examples below).\n\nYou can apply this logic to `field`, `div`, and `fieldset` elements.\n\nNote: any field names included within the\n`conditional_fields_to_show_as_required()` method will appear on the frontend\nas required, though will technically be `required=False`.\n\n**Field example:**\n\n```python\nfrom django import forms\nfrom django.core.exceptions import ValidationError\nfrom tbxforms.choices import Choice\nfrom tbxforms.forms import TbxFormsMixin\nfrom tbxforms.layout import Field, Layout\n\nclass ExampleForm(TbxFormsMixin, forms.Form):\n    NEWSLETTER_CHOICES = (\n        Choice(\"yes\", \"Yes please\", hint=\"Receive occasional email newsletters.\"),\n        Choice(\"no\", \"No thanks\"),\n    )\n\n    newsletter_signup = forms.ChoiceField(\n        choices=NEWSLETTER_CHOICES\n    )\n\n    email = forms.EmailField(\n        widget=forms.EmailInput(required=False)\n    )\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper.layout = Layout(\n            # Add our newsletter sign-up field.\n            Field(\"newsletter_signup\"),\n\n            # Add our email field and define the conditional logic.\n            Field(\n                \"email\",\n                data_conditional={\n                    \"field_name\": \"newsletter_signup\", # Field to inspect.\n                    \"values\": [\"yes\"], # Value(s) to cause this field to show.\n                },\n            ),\n        )\n\n    @staticmethod\n    def conditional_fields_to_show_as_required() -> [str]:\n        # Non-required fields that should show as required to the user.\n        return [\n            \"email\",\n        ]\n\n    def clean(self):\n        cleaned_data = super().clean()\n        newsletter_signup = cleaned_data.get(\"newsletter_signup\")\n        email = cleaned_data.get(\"email\")\n\n        # Fields included within `conditional_fields_to_show_as_required()` will\n        # be shown as required but not enforced - i.e. they will not have the\n        # HTML5 `required` attribute set.\n        # Thus we need to write our own check to enforce the value exists.\n        if newsletter_signup == \"yes\" and not email:\n            raise ValidationError(\n                {\n                    \"email\": \"This field is required.\",\n                }\n            )\n        # The tbxforms JS will attempt to clear any redundant data upon submission,\n        # though it is recommended to also handle this in your clean() method.\n        elif newsletter_signup == \"no\" and email:\n            del cleaned_data['email']\n\n        return cleaned_data\n\n```\n\n**Container example:**\n\nWhen you have multiple fields/elements that you want to show/hide together, you\ncan use the exact same `data_conditional` definition as above but on a `div` or\n`fieldset` element, e.g.:\n\n```python\nfrom tbxforms.layout import HTML, Div, Field, Layout\n\nLayout(\n    Div(\n        HTML(\"<p>Some relevant text.</p>\"),\n        Field(\"some_other_field\"),\n        Field(\"email\"),\n        data_conditional={\n            \"field_name\": \"newsletter_signup\",\n            \"values\": [\"yes\"],\n        },\n    ),\n)\n```\n\n## Customising behaviour\n\n### Allow HTML markup within labels and help text\n\nMarkup within labels and help text is disabled by default, though can be\nenabled via:\n\n```python\nTBXFORMS_ALLOW_HTML_LABEL = False\nTBXFORMS_ALLOW_HTML_HELP_TEXT = False\nTBXFORMS_ALLOW_HTML_BUTTON = False\n```\n\n### Change the default label and legend classes\n\nPossible values for the `label_size` and `legend_size`:\n\n1. `SMALL`\n2. `MEDIUM` (default)\n3. `LARGE`\n4. `EXTRA_LARGE`\n\n# Further reading\n\n-   Download the [PyPI package](http://pypi.python.org/pypi/tbxforms)\n-   Download the [NPM package](https://www.npmjs.com/package/tbxforms)\n-   Learn more about [Django Crispy Forms](https://django-crispy-forms.readthedocs.io/en/latest/)\n-   Learn more about [Crispy Forms GDS](https://github.com/wildfish/crispy-forms-gds)\n-   Learn more about [GOV.UK Design System](https://design-system.service.gov.uk/)\n",
    "bugtrack_url": null,
    "license": "BSD-2-Clause",
    "summary": "A Torchbox-flavoured template pack for django-crispy-forms, adapted from crispy-forms-gds",
    "version": "1.1.0",
    "project_urls": {
        "Homepage": "https://github.com/torchbox/tbxforms/",
        "Repository": "https://github.com/torchbox/tbxforms/"
    },
    "split_keywords": [
        "crispy",
        "django",
        "django crispy forms",
        "django crispy form",
        "forms",
        "gds",
        "tbxforms",
        "wagtail",
        "wagtail forms",
        "wagtail form"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9aa84bc735639bb5363ac5e051a538d9e602b6e961b41a81f24a5ec214a2ccf8",
                "md5": "5436a9c0dcc2c18c2e22c6415987e132",
                "sha256": "86f035e829b12b3d2e4ea32f7ed61399261e87eb02d872bbaa116f56fc49a800"
            },
            "downloads": -1,
            "filename": "tbxforms-1.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5436a9c0dcc2c18c2e22c6415987e132",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8.1,<4.0",
            "size": 52997,
            "upload_time": "2023-08-16T13:03:59",
            "upload_time_iso_8601": "2023-08-16T13:03:59.508875Z",
            "url": "https://files.pythonhosted.org/packages/9a/a8/4bc735639bb5363ac5e051a538d9e602b6e961b41a81f24a5ec214a2ccf8/tbxforms-1.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6b0a2100efc5c39238b1bdb00b3a3056122712049e6015e499f5633d17b5dadc",
                "md5": "d173caf0ed797926a4722d4d1320e84a",
                "sha256": "48275ce3bbbb689dd09b175b310baae3d538f3d162aea7d514c60ae18ec2cc35"
            },
            "downloads": -1,
            "filename": "tbxforms-1.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d173caf0ed797926a4722d4d1320e84a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8.1,<4.0",
            "size": 38714,
            "upload_time": "2023-08-16T13:04:01",
            "upload_time_iso_8601": "2023-08-16T13:04:01.472340Z",
            "url": "https://files.pythonhosted.org/packages/6b/0a/2100efc5c39238b1bdb00b3a3056122712049e6015e499f5633d17b5dadc/tbxforms-1.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-16 13:04:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "torchbox",
    "github_project": "tbxforms",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "tbxforms"
}
        
Elapsed time: 0.11525s