Skip to main content

A form templating app for Django

Project description

paper-forms

This library provides tools to simplify and customize the form rendering process in Django. It includes the BaseComposer class for centralized management of form settings and extends the functionality of BoundField for convenient customization of form fields.

PyPI Build Status Software license

Compatibility

  • python >= 3.9
  • django >= 3.2

Features

Table of Contents

  1. Installation
  2. Basic Usage
  3. Composer Configuration
    1. Specifying Custom Template Names
    2. Customizing Form Field Rendering in Composer
    3. Customizing Composer Class
  4. Template Tags
  5. Common Issues and Workarounds

Installation

To get started with paper-forms, you need to install the library using pip. Ensure you have Python 3.9 or later installed in your environment.

pip install paper-forms

Next, add "paper_forms" to the INSTALLED_APPS in your Django project's settings.py:

INSTALLED_APPS = (
    # ...
    "paper_forms",
)

Basic Usage

Start by creating a simple Django form. No mixins or third-party classes are required. Just define your form as you normally would using Django's forms module.

# forms.py
from django import forms

class ExampleForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()

Now, let's render the form in your template using the {% field %} template tag provided by paper-forms. This tag allows you to render form fields with enhanced customization.

<!-- example_template.html -->
{% load paper_forms %}

<form method="post">
  {% field form.name %}
  {% field form.age %}
</form>

The rendered HTML will be similar to the standard Django form rendering, but with the added flexibility provided by paper-forms. This sets the foundation for integrating paper-forms seamlessly into your Django project.

Composer Configuration

The real power of paper-forms lies in the ability to customize form rendering using the BaseComposer class. Let's explore how you can leverage this class to tailor the rendering of your forms.

One of the key features of paper-forms is the ability to customize form widgets. In your Django form, you can define a Composer class that inherits from BaseComposer to specify different widgets for specific fields.

# forms.py
from django import forms
from paper_forms.composer import BaseComposer

class ExampleForm(forms.Form):
    name = forms.CharField()
    password = forms.CharField()

    class Composer(BaseComposer):
        widgets = {
            "password": forms.PasswordInput,
        }

In this example, the Composer class defines a custom widget for the "password" field. This allows you to have fine-grained control over the rendering of individual form fields.

paper-forms also allows you to set labels and help text for form fields, either globally or on a per-field basis.

# forms.py
from django import forms
from paper_forms.composer import BaseComposer

class ExampleForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()

    class Composer(BaseComposer):
        labels = {
            "name": "Your Full Name",
            "age": "Your Age",
        }
        help_texts = {
            "name": "Enter your full name as it appears on official documents.",
            "age": "Please provide your current age.",
        }

Here, the Composer class provides labels and help text for the "name" and "age" fields, offering clear instructions to users interacting with your forms.

Additionally, developers can enhance the customization of the form's appearance by utilizing the error_css_class and required_css_class attributes within the Composer class. These attributes allow you to define specific CSS classes for handling errors and indicating required fields, respectively. Notably, any values set for these attributes in the Composer class take precedence over those specified at the form level.

Specifying Custom Template Names

When using paper-forms, you have the flexibility to create custom templates for individual form fields. This can be achieved by leveraging the Composer class and specifying custom template names for each field. Let's go through an example of how a custom form field template might look:

Assume you have a Django form with a field named "name," and you want to create a custom template for rendering this specific field.

  1. Create the Custom Template
    Create a new HTML file, let's call it custom_field.html, and place it in your Django app's templates directory. Here's a simplified example of how the template might look:
    <!-- custom_field.html -->
    
    <div class="form-field {{ css_classes }}">
        <label for="{{ widget.attrs.id }}">{{ label }}</label>
    
        <!-- include default widget template -->
        {% include widget.template_name %}
    
        <!-- show field errors -->
        {% if errors %}
        <ul>
            {% for error in errors %}
            <li>{{ error }}</li>
            {% endfor %}
        </ul>
        {% endif %}
    
        <!-- show help text -->
        {% if help_text %}
        <small>{{ help_text }}</small>
        {% endif %}
    </div>
    
    In this example, the template includes a custom structure for rendering the form field, including a label, the actual form widget, error messages, and help text.
  2. Update the Composer Class
    Now, update the Composer class in your Django form to specify the custom template name for the "name" field:
    from django import forms
    from paper_forms.composer import BaseComposer
    
    class ExampleForm(forms.Form):
        name = forms.CharField()
        age = forms.IntegerField()
    
        class Composer(BaseComposer):
            template_names = {
                "name": "path/to/custom_field.html",
            }
    
    Ensure that the path provided in template_names matches the location of your custom template file.
  3. Render the Form in Your Template
    In your Django template where you render the form, use the {% field %} template tag for the "name" field:
    {% load paper_forms %}
    
    <form method="post">
        {% field form.name %}
        {% field form.age %}
        <button type="submit">Submit</button>
    </form>
    
    When the form is rendered, the "name" field will use your custom template, while other fields will follow the default rendering.

By following these steps, you can create and apply custom templates for specific form fields, providing a tailored look and feel for different parts of your Django forms.

Customizing Form Field Rendering in Composer

The paper-forms library provides extensive flexibility through the Composer class, allowing you to customize the rendering of form fields. In addition to specifying custom templates, you can achieve advanced customization by overriding methods in the BaseComposer class. Let's explore an example of how to override methods to replace labels with placeholders across all form fields.

Suppose you want to make a global change to the rendering of form fields, replacing labels with placeholders.

Step 1: Create a Custom Composer.

Create a custom Composer subclass with the desired customization for form field rendering.

# composers.py
from paper_forms.composer import BaseComposer

class CustomComposer(BaseComposer):
    placeholders: dict = None

    def get_label(self, name, widget):
        # Override get_label method to return an empty string 
        # for all fields.
        return ""

    def build_widget_attrs(self, name, attrs, widget):
        attrs = super().build_widget_attrs(name, attrs, widget)
        placeholder = attrs.get("placeholder")
        if placeholder is None and self.placeholders:
            placeholder = self.placeholders[name]
        if placeholder:
            attrs["placeholder"] = placeholder
        return attrs

Step 2: Use Custom Composer in a Django Form.

Apply the custom CustomComposer class to a Django form.

# forms.py
from django import forms
from .composers import CustomComposer


class ExampleForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()

    class Composer(CustomComposer):
        placeholders = {
            "name": "Enter your name",
            "age": "Enter your age",
        }

Step 3: Render the Form in Your Template.

Use the {% field %} template tag to render the form in your template.

{% load paper_forms %}

<form method="post">
    {% field form.name %}
    {% field form.age %}
    <button type="submit">Submit</button>
</form>

With this setup, the "name" and "age" fields will have placeholders instead of labels.

Customizing Composer Class

In addition to the specific methods discussed earlier, the BaseComposer class in the paper-forms library provides several other methods that developers can override to further customize form field rendering. Below are some of the additional methods available for customization:

get_widget(self, name: str) -> Widget

Retrieves a widget for a specific form field. By default, it looks up the widget in the widgets dictionary. If None is returned, the default widget for the field will be used.

get_template_name(self, name: str, widget: Widget) -> str

Determines the template name to be used for rendering a form field. It considers hidden widgets, specific template names in the template_names dictionary, and falls back to the default template name.

get_default_template_name(self, name: str, widget: Widget) -> str

This method plays a crucial role in simplifying the creation of Composer classes for frameworks like Bootstrap. This method is designed to facilitate the customization of form field templates without affecting the ability to specify template paths via the template_names attribute in the Composer class.

When rendering a form field, the get_default_template_name method is called to determine the default template name based on the provided field name and widget. This method can be overridden in custom Composer classes to set a default template name, allowing for a more streamlined implementation of framework-specific Composers.

from django.forms.widgets import TextInput, NumberInput
from paper_forms.composer import BaseComposer

class FrameworkComposer(BaseComposer):
    def get_default_template_name(self, name, widget) -> str:
        if isinstance(widget, TextInput):
            return f"bootstrap/text_field.html"
        elif isinstance(widget, NumberInput):
            return f"bootstrap/number_field.html"

        # Fall back to the default behavior
        return super().get_default_template_name(name, widget)

get_label(self, name: str, widget: Widget) -> Optional[str]

Retrieves the label for a form field. It looks up the label in the labels dictionary. If None is returned, the default label for the field will be used.

get_help_text(self, name: str, widget: Widget) -> Optional[str]

Retrieves the help text for a form field. It looks up the help text in the help_texts dictionary. If None is returned, the default help text for the field will be used.

get_css_classes(self, name: str, widget: Widget) -> Optional[str]

Retrieves the CSS classes for a form field. It looks up the CSS classes in the css_classes dictionary.

build_widget_attrs(self, name: str, attrs: Optional[dict], widget: Widget) -> dict

Builds and customizes the attributes for a form field's widget. Developers can add, remove, or modify attributes based on field names or other criteria.

build_context(self, name: str, context: Optional[dict], widget: Widget) -> dict

Builds the context to be passed to the form field template. Developers can add or modify context variables based on field names or other conditions.

Template Tags

paper-forms provides template tags to simplify the integration of the library into your Django templates. The primary tag is {% field %}, which allows you to render form fields with enhanced customization options.

The {% field %} tag is the key to leveraging the features provided by paper-forms. It allows you to render form fields with various attributes and context variables.

{% load paper_forms %}

<form method="post">
  {% field form.name placeholder="Enter your name" %}
  {% field form.age placeholder="Enter your age" _style="light" %}
</form>

In this example, the {% field %} tag is used to render the "name" and "age" fields of the form. Attributes such as placeholder and style are passed as parameters to customize the rendering.

The {% field %} tag supports adding variables to the form field template using the "_" prefix in the template tag parameters. These variables become part of the template context for the form field.

In the example above, the placeholder attribute is a widget attribute, while the _style is a template context variable. Parameters with a leading underscore, such as _style, are treated as template context variables.

Configuration

paper-forms provides additional configuration options that you can set in your Django project's settings.

You can specify the default Composer class to be used for any form that doesn't specify a particular composer. This is set using the PAPER_FORMS_DEFAULT_COMPOSER setting.

# settings.py

PAPER_FORMS_DEFAULT_COMPOSER = "myapp.composers.CustomComposer"

Here, myapp.composers.CustomComposer is the custom composer class you want to use as the default.

The default form renderer can be set using the PAPER_FORMS_DEFAULT_FORM_RENDERER setting.

# settings.py

PAPER_FORMS_DEFAULT_FORM_RENDERER = "django.forms.renderers.TemplatesSetting"

In this example, django.forms.renderers.TemplatesSetting is used as the default form renderer.

Common Issues and Workarounds

In the course of using paper-forms, you may encounter some common issues. This section provides explanations and workarounds for these issues to help you navigate potential challenges.

Dashes in Attribute Names

If you are trying to specify an attribute with dashes in the {% field %} template tag, like data-src, you might face limitations. This is due to the restrictive nature of @simple_tag, which does not allow dashes in kwargs names.

To overcome this limitation, use double underscores. All double underscores in {% field %} arguments are replaced with single dashes. For example:

{% field form.name data__original__name="Name" %}

This would render to something like

<input ... data-original-name="Name" />

FORM_RENDERER Setting with Third-Party Template Engines

If you are using django-jinja or any other third-party template engine as your default template engine, and you want to use it for your form field templates, you might face challenges. Django's form widgets are rendered using form renderers, and changing the FORM_RENDERER setting can break the admin interface.

Follow these steps to work around this problem:

  1. Make built-in widget templates searchable:

    # settings.py
    
    from pathlib import Path
    from django import forms
    
    TEMPLATES = [
        {
            "NAME": "jinja2",
            "BACKEND": "django_jinja.backend.Jinja2",
            "DIRS": [
                BASE_DIR / "templates",
                Path(forms.__file__).parent / "jinja2"
            ],
            # ...
        }
    ]
    
  2. Use TemplateSetting renderer for your forms or implement your own. You can achieve this in several ways:

    • Set the PAPER_FORMS_DEFAULT_FORM_RENDERER setting in settings.py:
      # settings.py
      
      PAPER_FORMS_DEFAULT_FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
      
    • Specify the default_renderer attribute in your form:
      from django import forms
      from django.forms.renderers import TemplatesSetting
      
      class ExampleForm(forms.Form):
          default_renderer = TemplatesSetting
          # ...
      
    • Set the renderer field in your Composer class:
      from django import forms
      from paper_forms.composer import BaseComposer
      
      class ExampleForm(forms.Form):
          name = forms.CharField()
      
          class Composer(BaseComposer):
              renderer = "django.forms.renderers.TemplatesSetting"
      

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paper_forms-0.5.3.tar.gz (18.0 kB view hashes)

Uploaded Source

Built Distribution

paper_forms-0.5.3-py2.py3-none-any.whl (12.2 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page