Fixed #29956 -- Allowed overriding an order field widget in formsets.
This commit is contained in:
parent
413d50b5ff
commit
5fc5d93512
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~
|
||||
|
|
|
@ -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``
|
||||
--------------
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue