Carl Meyer avatar Carl Meyer committed 3f53bd5

Add several useful filters for form-rendering.

Comments (0)

Files changed (4)

form_utils/templatetags/form_utils_tags.py

 Time-stamp: <2009-03-26 12:32:08 carljm form_utils_tags.py>
 
 """
+from django import forms
 from django import template
+from django.template.loader import render_to_string
 
 from form_utils.forms import BetterForm, BetterModelForm
 from form_utils.utils import select_template_from_string
     ``form_utils.forms.BetterForm`` or
     ``form_utils.forms.BetterModelForm``, the template
     ``form_utils/better_form.html`` will be used instead if present.
-    
+
     """
     default = 'form_utils/form.html'
     if isinstance(form, (BetterForm, BetterModelForm)):
 
     return tpl.render(template.Context({'form': form}))
 
-        
 
-    
+
+
+@register.filter
+def placeholder(boundfield, value):
+    """Set placeholder attribute for given boundfield."""
+    boundfield.field.widget.attrs["placeholder"] = value
+    return boundfield
+
+
+
+@register.filter
+def label(boundfield, contents=None):
+    """Render label tag for a boundfield, optionally with given contents."""
+    label_text = contents or boundfield.label
+    id_ = boundfield.field.widget.attrs.get('id') or boundfield.auto_id
+
+    return render_to_string(
+        "forms/_label.html",
+        {
+            "label_text": label_text,
+            "id": id_,
+            "field": boundfield,
+            })
+
+
+
+@register.filter
+def label_text(boundfield):
+    """Return the default label text for the given boundfield."""
+    return boundfield.label
+
+
+
+@register.filter
+def value_text(boundfield):
+    """Return the value for given boundfield as human-readable text."""
+    val = boundfield.value()
+    # If choices is set, use the display label
+    return unicode(
+        dict(getattr(boundfield.field, "choices", [])).get(val, val))
+
+
+
+@register.filter
+def values_text(boundfield):
+    """Return the values for given multiple-select as human-readable text."""
+    val = boundfield.value()
+    # If choices is set, use the display label
+    choice_dict = dict(getattr(boundfield.field, "choices", []))
+    return [unicode(choice_dict.get(v, v)) for v in val]
+
+
+
+@register.filter
+def classes(boundfield, classes):
+    """Append given classes to the widget attrs of given boundfield."""
+    attrs = boundfield.field.widget.attrs
+    attrs["class"] = " ".join(
+        [c for c in [attrs.get("class", None), classes] if c])
+    return boundfield
+
+
+
+@register.filter
+def optional(boundfield):
+    """Return True if given boundfield is optional, else False."""
+    return not boundfield.field.required
+
+
+
+@register.filter
+def is_checkbox(boundfield):
+    """Return True if this field's widget is a CheckboxInput."""
+    return isinstance(
+        boundfield.field.widget, forms.CheckboxInput)
+
+
+
+@register.filter
+def is_multiple(boundfield):
+    """Return True if this field has multiple values."""
+    return isinstance(boundfield.field.widget, forms.SelectMultiple)
 import os.path
 
 try:
-    # don't get confused if our sdist is unzipped in a subdir of some 
+    # don't get confused if our sdist is unzipped in a subdir of some
     # other hg repo
     if os.path.isdir('.hg'):
         p = subprocess.Popen(['hg', 'parents', r'--template={rev}\n'],
             fh.close()
 except (OSError, IndexError):
     pass
-    
+
 try:
     hgrev = open('HGREV').read()
 except IOError:
     hgrev = ''
-    
+
 long_description = open('README.rst').read() + open('CHANGES.rst').read()
 
 setup(
     zip_safe=False,
     package_data={'form_utils': ['templates/form_utils/*.html',
                                  'media/form_utils/js/*.js']},
-    test_suite='tests.runtests.runtests'
+    test_suite='tests.runtests.runtests',
+    tests_require=['Django', 'mock'],
 )
 from django.db.models.fields.files import FieldFile, ImageFieldFile, FileField, ImageField
 from django.test import TestCase
 
+from mock import patch
+
 from form_utils.forms import BetterForm, BetterModelForm
 from form_utils.widgets import ImageWidget, ClearableFileInput
 from form_utils.fields import ClearableFileField, ClearableImageField
 
-from models import Person, Document
+from .models import Person, Document
 
 class ApplicationForm(BetterForm):
     """
         """
         self.assertEqual(ManualPartialPersonForm._meta.fields, ['name', 'age'])
 
-    def test_modelform_fields(self):
+    def test_modelform_fields_exclude(self):
         """
         The ``fields`` Meta option of a ModelForm is not automatically
         populated if ``exclude`` is set manually.
             widget = ClearableImageWidget
         field = ClearableImageWidgetField()
         self.assertTrue(isinstance(field.widget, ClearableImageWidget))
+
+
+
+
+class FieldFilterTests(TestCase):
+    """Tests for form field filters."""
+    @property
+    def form_utils_tags(self):
+        """The module under test."""
+        from form_utils.templatetags import form_utils_tags
+        return form_utils_tags
+
+
+    @property
+    def form(self):
+        """A sample form."""
+        class PersonForm(forms.Form):
+            name = forms.CharField(initial="none", required=True)
+            level = forms.ChoiceField(
+                choices=(("b", "Beginner"), ("a", "Advanced")), required=False)
+            awesome = forms.BooleanField(required=False)
+
+        return PersonForm
+
+
+
+    def test_placeholder(self):
+        """``placeholder`` filter sets placeholder attribute."""
+        bf = self.form_utils_tags.placeholder(self.form()["name"], "Placeholder")
+        self.assertIn('placeholder="Placeholder"', unicode(bf))
+
+
+    @patch("form_utils.templatetags.form_utils_tags.render_to_string")
+    def test_label(self, render_to_string):
+        """``label`` filter renders field label from template."""
+        render_to_string.return_value = "<label>something</label>"
+        bf = self.form()["name"]
+
+        label = self.form_utils_tags.label(bf)
+
+        self.assertEqual(label, "<label>something</label>")
+        render_to_string.assert_called_with(
+            "forms/_label.html",
+            {
+                "label_text": "Name",
+                "id": "id_name",
+                "field": bf
+                }
+            )
+
+
+    @patch("form_utils.templatetags.form_utils_tags.render_to_string")
+    def test_label_override(self, render_to_string):
+        """label filter allows overriding the label text."""
+        bf = self.form()["name"]
+
+        self.form_utils_tags.label(bf, "override")
+
+        render_to_string.assert_called_with(
+            "forms/_label.html",
+            {
+                "label_text": "override",
+                "id": "id_name",
+                "field": bf
+                }
+            )
+
+    def test_label_text(self):
+        """``label_text`` filter returns field's default label text."""
+        self.assertEqual(self.form_utils_tags.label_text(self.form()["name"]), "Name")
+
+
+    def test_value_text(self):
+        """``value_text`` filter returns value of field."""
+        self.assertEqual(
+            self.form_utils_tags.value_text(self.form({"name": "boo"})["name"]), "boo")
+
+
+    def test_value_text_unbound(self):
+        """``value_text`` filter returns default value of unbound field."""
+        self.assertEqual(self.form_utils_tags.value_text(self.form()["name"]), "none")
+
+
+    def test_value_text_choices(self):
+        """``value_text`` filter returns human-readable value of choicefield."""
+        self.assertEqual(
+            self.form_utils_tags.value_text(
+                self.form({"level": "a"})["level"]), "Advanced")
+
+
+    def test_values_text_choices(self):
+        """``values_text`` filter returns values of multiple select."""
+        f = self.form({"level": ["a", "b"]})
+
+        self.assertEqual(
+            self.form_utils_tags.values_text(f["level"]), ["Advanced", "Beginner"])
+
+
+    def test_optional_false(self):
+        """A required field should not be marked optional."""
+        self.assertFalse(self.form_utils_tags.optional(self.form()["name"]))
+
+
+    def test_optional_true(self):
+        """A non-required field should be marked optional."""
+        self.assertTrue(self.form_utils_tags.optional(self.form()["level"]))
+
+
+    def test_detect_checkbox(self):
+        """``is_checkbox`` detects checkboxes."""
+        f = self.form()
+
+        self.assertTrue(self.form_utils_tags.is_checkbox(f["awesome"]))
+
+
+    def test_detect_non_checkbox(self):
+        """``is_checkbox`` detects that select fields are not checkboxes."""
+        f = self.form()
+
+        self.assertFalse(self.form_utils_tags.is_checkbox(f["level"]))
+
+
+    def test_is_multiple(self):
+        """`is_multiple` detects a SelectMultiple widget."""
+        f = self.form()
+        f.fields["level"].widget = forms.SelectMultiple()
+
+        self.assertTrue(self.form_utils_tags.is_multiple(f["level"]))
+
+
+    def test_is_not_multiple(self):
+        """`is_multiple` detects a non-multiple widget."""
+        f = self.form()
+
+        self.assertFalse(self.form_utils_tags.is_multiple(f["level"]))
   django==1.4.1
   South==0.7.6
   PIL==1.1.7
+  mock==1.0b1
 commands=python setup.py test
 
 [testenv:py26-1.3]
   django==1.3.3
   South==0.7.6
   PIL==1.1.7
+  mock==1.0b1
 
 [testenv:py26-trunk]
 basepython=python2.6
   https://github.com/django/django/tarball/master
   South==0.7.6
   PIL==1.1.7
+  mock==1.0b1
 
 [testenv:py27-1.3]
 basepython=python2.7
   django==1.3.3
   South==0.7.6
   PIL==1.1.7
+  mock==1.0b1
 
 [testenv:py27-trunk]
 basepython=python2.7
   https://github.com/django/django/tarball/master
   PIL==1.1.7
   South==0.7.6
+  mock==1.0b1
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.