# HG changeset patch # User Branko Majic # Date 2013-07-24 21:16:13 # Node ID 529cb3e7db9bd67adebf97dc05a7a5e62dc7ff3c # Parent 9a15715b4674259ac5c62be18f3ef8cc4cc62eb7 CONNT-13: Moved the CSS styling and text input placeholder setting into auxiliary form mixin clases. Update existing forms and tests. diff --git a/conntrackt/forms.py b/conntrackt/forms.py --- a/conntrackt/forms.py +++ b/conntrackt/forms.py @@ -1,12 +1,72 @@ # Django imports. from django.forms import ModelForm -from django.forms.models import inlineformset_factory # Application imports. from .models import Entity, Interface, Communication -class EntityForm(ModelForm): +class WidgetCSSClassFormMixin(object): + """ + Helper form mixin that can be used for applying additional custom CSS + classes to form field widgets. + + The mixin accepts the following class options: + + widget_css_classes - Dictionary describing which additional CSS classes + should be applied to which form widget. Dictinoary keys should be equal + to form widget names, while the value should be a string containing the + extra CSS classes that should be applied to it. In order to apply the + CSS classes to every widget of the form, use the key "ALL" + """ + + def __init__(self, *args, **kwargs): + """ + Assigns the custom CSS classes form widgets, as configured by the + widget_css_classes property. + """ + + super(WidgetCSSClassFormMixin, self).__init__(*args, **kwargs) + + for field_name, css_class in self.widget_css_classes.iteritems(): + if field_name == "ALL": + for field in self.fields.values(): + if "class" in field.widget.attrs: + field.widget.attrs["class"] += " " + css_class + else: + field.widget.attrs["class"] = css_class + else: + field = self.fields[field_name] + if "class" in field.widget.attrs: + field.widget.attrs["class"] += " " + css_class + else: + field.widget.attrs["class"] = css_class + + +class PlaceholderFormMixin(object): + """ + Helper form mixin that can be used to set-up placeholders for text widgets. + + The mixin accepts the following class options: + + widget_placeholders - Dictionary describing which placeholders should be + applied to which widgets. Keys should be equal to form widget names, + while the values should be the strings that should be set as + placeholders. + """ + + def __init__(self, *args, **kwargs): + """ + Assigns the placeholders to text form widgets, as configured by the + widget_placeholders property. + """ + + super(PlaceholderFormMixin, self).__init__(*args, **kwargs) + + for field_name, placeholder in self.widget_placeholders.iteritems(): + self.fields[field_name].widget.attrs["placeholder"] = placeholder + + +class EntityForm(WidgetCSSClassFormMixin, PlaceholderFormMixin, ModelForm): """ Implements a custom model form for entities with some styling changes. """ @@ -14,24 +74,12 @@ class EntityForm(ModelForm): class Meta: model = Entity - def __init__(self, *args, **kwargs): - """ - Initialises the form instance. Sets-up some bootstrap CSS classes for - widgets. - """ - - super(EntityForm, self).__init__(*args, **kwargs) - - # Update the widgets to be wider. - for field_name, field in self.fields.iteritems(): - field.widget.attrs["class"] = "span6" - - # Set-up some placeholders. - self.fields["name"].widget.attrs["placeholder"] = "Entity name" - self.fields["description"].widget.attrs["placeholder"] = "Entity description" + widget_placeholders = {"name": "Entity name", + "description": "Entity description"} + widget_css_classes = {"ALL": "span6"} -class InterfaceForm(ModelForm): +class InterfaceForm(WidgetCSSClassFormMixin, PlaceholderFormMixin, ModelForm): """ Implements a custom model form for interfaces with some styling changes. """ @@ -39,26 +87,15 @@ class InterfaceForm(ModelForm): class Meta: model = Interface - def __init__(self, *args, **kwargs): - """ - Initialises the form instance. Sets-up some bootstrap CSS classes for - widgets. - """ - - super(InterfaceForm, self).__init__(*args, **kwargs) + widget_placeholders = {"name": "Interface name", + "description": "Interface description", + "address": "IP address of interface", + "netmask": "IP address netmask"} - # Update the widgets to be wider. - for field_name, field in self.fields.iteritems(): - field.widget.attrs["class"] = "span6" - - # Set-up some placeholders. - self.fields["name"].widget.attrs["placeholder"] = "Interface name" - self.fields["description"].widget.attrs["placeholder"] = "Interface description" - self.fields["address"].widget.attrs["placeholder"] = "IP address of interface" - self.fields["netmask"].widget.attrs["placeholder"] = "IP address netmask" + widget_css_classes = {"ALL": "span6"} -class CommunicationForm(ModelForm): +class CommunicationForm(WidgetCSSClassFormMixin, PlaceholderFormMixin, ModelForm): """ Implements a custom model form for communications with some styling changes. """ @@ -66,18 +103,7 @@ class CommunicationForm(ModelForm): class Meta: model = Communication - def __init__(self, *args, **kwargs): - """ - Initialises the form instance. Sets-up some bootstrap CSS classes for - widgets. - """ - - super(CommunicationForm, self).__init__(*args, **kwargs) + widget_placeholders = {"port": "Port used for communication", + "description": "Communication description"} - # Update the widgets to be wider. - for field_name, field in self.fields.iteritems(): - field.widget.attrs["class"] = "span6" - - # Set-up some placeholders. - self.fields["port"].widget.attrs["placeholder"] = "Port used for communication" - self.fields["description"].widget.attrs["placeholder"] = "Communication description" + widget_css_classes = {"ALL": "span6"} diff --git a/conntrackt/tests/forms.py b/conntrackt/tests/forms.py new file mode 100644 --- /dev/null +++ b/conntrackt/tests/forms.py @@ -0,0 +1,20 @@ +# Django imports +from django import forms + +# Application imports. +from conntrackt.forms import WidgetCSSClassFormMixin, PlaceholderFormMixin + + +class FormWithWidgetCSSClassFormMixin(WidgetCSSClassFormMixin, forms.Form): + """ + Helper form for testing the WidgetCSSClassFormMixin. + """ + + field1 = forms.CharField() + field2 = forms.CharField() + + +class FormWithPlaceholderFormMixin(PlaceholderFormMixin, forms.Form): + + field1 = forms.CharField() + field2 = forms.CharField() diff --git a/conntrackt/tests/test_forms.py b/conntrackt/tests/test_forms.py --- a/conntrackt/tests/test_forms.py +++ b/conntrackt/tests/test_forms.py @@ -1,23 +1,107 @@ # Django imports. from django.test import TestCase +# Python third-party library imports. +import mock + # Application imports. -from conntrackt.forms import EntityForm +from conntrackt.forms import WidgetCSSClassFormMixin, PlaceholderFormMixin +from conntrackt.forms import EntityForm, InterfaceForm, CommunicationForm + +# Test imports. +from .forms import FormWithWidgetCSSClassFormMixin, FormWithPlaceholderFormMixin -class EntityFormTest(TestCase): +class WidgetCSSClassFormMixinTest(TestCase): """ - Tests for the custom Entity model form. + Tests the form mixin. """ - def test_styling(self): + def test_apply_to_all(self): + """ + Test if CSS class is appled to all form field widgets. + """ + + # Set-up the form. + form_class = FormWithWidgetCSSClassFormMixin + form_class.widget_css_classes = {"ALL": "test"} + form = form_class() + + self.assertEqual(form.fields["field1"].widget.attrs["class"], "test") + self.assertEqual(form.fields["field2"].widget.attrs["class"], "test") + + def test_apply_to_single(self): """ - Test that the form styling is set-up correctly. + Test if CSS class is appled to a single field widget. + """ + + # Set-up the form. + form_class = FormWithWidgetCSSClassFormMixin + form_class.widget_css_classes = {"field2": "test"} + form = form_class() + + self.assertEqual(form.fields["field1"].widget.attrs.get("class", None), None) + self.assertEqual(form.fields["field2"].widget.attrs["class"], "test") + + def test_apply_multiple(self): + """ + Tests if different class is applied to multiple form field widgets. """ - form = EntityForm() + # Set-up the form. + form_class = FormWithWidgetCSSClassFormMixin + form_class.widget_css_classes = {"field1": "f1", + "field2": "f2"} + form = form_class() + + self.assertEqual(form.fields["field1"].widget.attrs["class"], "f1") + self.assertEqual(form.fields["field2"].widget.attrs["class"], "f2") + + def test_apply_to_all_additional(self): + """ + Tests if all widgets get the same CSS class in addition to induvidual + ones. + """ + + # Set-up the form. + form_class = FormWithWidgetCSSClassFormMixin + form_class.widget_css_classes = {"field1": "f1", + "field2": "f2", + "ALL": "all"} + form = form_class() + + self.assertEqual(sorted(["f1", "all"]), sorted(form.fields["field1"].widget.attrs["class"].split(" "))) + self.assertEqual(sorted(["f2", "all"]), sorted(form.fields["field2"].widget.attrs["class"].split(" "))) + - self.assertIn("span6", form.fields["name"].widget.attrs["class"]) - self.assertIn("span6", form.fields["description"].widget.attrs["class"]) - self.assertIn("span6", form.fields["project"].widget.attrs["class"]) - self.assertIn("span6", form.fields["location"].widget.attrs["class"]) +class PlaceholderFormMixinTest(TestCase): + """ + Tests the form mixin. + """ + + def test_apply_one(self): + """ + Test if a single placeholder is applied to a form field widget. + """ + + # Set-up the form. + form_class = FormWithPlaceholderFormMixin + form_class.widget_placeholders = {"field1": "place1"} + form = form_class() + + self.assertEqual(form.fields["field1"].widget.attrs["placeholder"], "place1") + self.assertEqual(form.fields["field2"].widget.attrs.get("placeholder", None), None) + + def test_apply_multiple(self): + """ + Test if multiple placeholders are applied to form field widgets. + """ + + # Set-up the form. + form_class = FormWithPlaceholderFormMixin + form_class.widget_placeholders = {"field1": "place1", + "field2": "place2"} + form = form_class() + + self.assertEqual(form.fields["field1"].widget.attrs["placeholder"], "place1") + self.assertEqual(form.fields["field2"].widget.attrs["placeholder"], "place2")