summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2018-11-21 21:58:04 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-03-21 11:51:01 +0100
commit5fc5d935120a5d9d428b969ad209695704c10c73 (patch)
treebdb846ae83e6bf391402ed1973cb4b728922f689
parent413d50b5ff0c0a4198d4bf069e6434bc7ba4cd86 (diff)
Fixed #29956 -- Allowed overriding an order field widget in formsets.
-rw-r--r--django/forms/formsets.py21
-rw-r--r--docs/releases/3.0.txt5
-rw-r--r--docs/topics/forms/formsets.txt43
-rw-r--r--tests/forms_tests/tests/test_formsets.py26
4 files changed, 91 insertions, 4 deletions
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index ed93fa852e..44c2cb07f4 100644
--- a/django/forms/formsets.py
+++ b/django/forms/formsets.py
@@ -2,7 +2,7 @@ from django.core.exceptions import ValidationError
from django.forms import Form
from django.forms.fields import BooleanField, IntegerField
from django.forms.utils import ErrorList
-from django.forms.widgets import HiddenInput
+from django.forms.widgets import HiddenInput, NumberInput
from django.utils.functional import cached_property
from django.utils.html import html_safe
from django.utils.safestring import mark_safe
@@ -47,6 +47,8 @@ class BaseFormSet:
"""
A collection of instances of the same Form class.
"""
+ ordering_widget = NumberInput
+
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, form_kwargs=None):
self.is_bound = data is not None or files is not None
@@ -264,6 +266,10 @@ class BaseFormSet:
def get_default_prefix(cls):
return 'form'
+ @classmethod
+ def get_ordering_widget(cls):
+ return cls.ordering_widget
+
def non_form_errors(self):
"""
Return an ErrorList of errors that aren't associated with a particular
@@ -368,9 +374,18 @@ class BaseFormSet:
if self.can_order:
# Only pre-fill the ordering field for initial forms.
if index is not None and index < self.initial_form_count():
- form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), initial=index + 1, required=False)
+ form.fields[ORDERING_FIELD_NAME] = IntegerField(
+ label=_('Order'),
+ initial=index + 1,
+ required=False,
+ widget=self.get_ordering_widget(),
+ )
else:
- form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), required=False)
+ form.fields[ORDERING_FIELD_NAME] = IntegerField(
+ label=_('Order'),
+ required=False,
+ widget=self.get_ordering_widget(),
+ )
if self.can_delete:
form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False)
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 71a8aef971..cf50eea1b5 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -139,7 +139,10 @@ File Uploads
Forms
~~~~~
-* ...
+* Formsets may control the widget used when ordering forms via
+ :attr:`~django.forms.formsets.BaseFormSet.can_order` by setting the
+ :attr:`~django.forms.formsets.BaseFormSet.ordering_widget` attribute or
+ overriding :attr:`~django.forms.formsets.BaseFormSet.get_ordering_widget()`.
Generic Views
~~~~~~~~~~~~~
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 0c4c7f0bee..b9c8721bf5 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -448,6 +448,49 @@ happen when the user changes these values::
{'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': 'Article #2'}
{'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': 'Article #1'}
+:class:`~django.forms.formsets.BaseFormSet` also provides an
+:attr:`~django.forms.formsets.BaseFormSet.ordering_widget` attribute and
+:meth:`~django.forms.formsets.BaseFormSet.get_ordering_widget` method that
+control the widget used with
+:attr:`~django.forms.formsets.BaseFormSet.can_order`.
+
+``ordering_widget``
+^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.0
+
+.. attribute:: BaseFormSet.ordering_widget
+
+Default: :class:`~django.forms.NumberInput`
+
+Set ``ordering_widget`` to specify the widget class to be used with
+``can_order``::
+
+ >>> from django.forms import BaseFormSet, formset_factory
+ >>> from myapp.forms import ArticleForm
+ >>> class BaseArticleFormSet(BaseFormSet):
+ ... ordering_widget = HiddenInput
+
+ >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True)
+
+``get_ordering_widget``
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.0
+
+.. method:: BaseFormSet.get_ordering_widget()
+
+Override ``get_ordering_widget()`` if you need to provide a widget instance for
+use with ``can_order``::
+
+ >>> from django.forms import BaseFormSet, formset_factory
+ >>> from myapp.forms import ArticleForm
+ >>> class BaseArticleFormSet(BaseFormSet):
+ ... def get_ordering_widget(self):
+ ... return HiddenInput(attrs={'class': 'ordering'})
+
+ >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True)
+
``can_delete``
--------------
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index 231436f73e..7c6a244c90 100644
--- a/tests/forms_tests/tests/test_formsets.py
+++ b/tests/forms_tests/tests/test_formsets.py
@@ -8,6 +8,7 @@ from django.forms import (
)
from django.forms.formsets import BaseFormSet, all_valid, formset_factory
from django.forms.utils import ErrorList
+from django.forms.widgets import HiddenInput
from django.test import SimpleTestCase
@@ -591,6 +592,31 @@ class FormsFormsetTestCase(SimpleTestCase):
],
)
+ def test_formsets_with_order_custom_widget(self):
+ class OrderingAttributFormSet(BaseFormSet):
+ ordering_widget = HiddenInput
+
+ class OrderingMethodFormSet(BaseFormSet):
+ def get_ordering_widget(self):
+ return HiddenInput(attrs={'class': 'ordering'})
+
+ tests = (
+ (OrderingAttributFormSet, '<input type="hidden" name="form-0-ORDER">'),
+ (OrderingMethodFormSet, '<input class="ordering" type="hidden" name="form-0-ORDER">'),
+ )
+ for formset_class, order_html in tests:
+ with self.subTest(formset_class=formset_class):
+ ArticleFormSet = formset_factory(ArticleForm, formset=formset_class, can_order=True)
+ formset = ArticleFormSet(auto_id=False)
+ self.assertHTMLEqual(
+ '\n'.join(form.as_ul() for form in formset.forms),
+ (
+ '<li>Title: <input type="text" name="form-0-title"></li>'
+ '<li>Pub date: <input type="text" name="form-0-pub_date">'
+ '%s</li>' % order_html
+ ),
+ )
+
def test_empty_ordered_fields(self):
"""
Ordering fields are allowed to be left blank. If they are left blank,