Commit 0d1ba84d authored by tschilling's avatar tschilling Committed by Tim Graham
Browse files

Fixed #20702 -- Deprecated get_formsets in favor of get_formsets_with_inlines.

Thanks stanislas.guerra at gmail.com for the report.
parent f8f47718
Loading
Loading
Loading
Loading
+48 −11
Original line number Diff line number Diff line
@@ -602,10 +602,48 @@ class ModelAdmin(BaseModelAdmin):
            self.get_changelist_form(request), extra=0,
            fields=self.list_editable, **defaults)

    def get_formsets(self, request, obj=None):
    def _get_formsets(self, request, obj):
        """
        Helper function that exists to allow the deprecation warning to be
        executed while this function continues to return a generator.
        """
        for inline in self.get_inline_instances(request, obj):
            yield inline.get_formset(request, obj)

    def get_formsets(self, request, obj=None):
        warnings.warn(
            "ModelAdmin.get_formsets() is deprecated and will be removed in "
            "Django 1.9. Use ModelAdmin.get_formsets_with_inlines() instead.",
            PendingDeprecationWarning, stacklevel=2
        )
        return self._get_formsets(request, obj)

    def get_formsets_with_inlines(self, request, obj=None):
        """
        Yields formsets and the corresponding inlines.
        """
        # We call get_formsets() [deprecated] and check if it triggers a
        # warning. If it does, then it's ours and we can safely ignore it, but
        # if it doesn't then it has been overridden so we must warn about the
        # deprecation.
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            formsets = self.get_formsets(request, obj)

        if len(w) != 1 or not issubclass(w[0].category, PendingDeprecationWarning):
            warnings.warn(
                "ModelAdmin.get_formsets() is deprecated and will be removed in "
                "Django 1.9. Use ModelAdmin.get_formsets_with_inlines() instead.",
                PendingDeprecationWarning
            )
            if formsets:
                zipped = zip(formsets, self.get_inline_instances(request, None))
                for formset, inline in zipped:
                    yield formset, inline
        else:
            for inline in self.get_inline_instances(request, obj):
                yield inline.get_formset(request, obj), inline

    def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
        return self.paginator(queryset, per_page, orphans, allow_empty_first_page)

@@ -1185,8 +1223,6 @@ class ModelAdmin(BaseModelAdmin):
            raise PermissionDenied

        ModelForm = self.get_form(request)
        formsets = []
        inline_instances = self.get_inline_instances(request, None)
        if request.method == 'POST':
            form = ModelForm(request.POST, request.FILES)
            if form.is_valid():
@@ -1195,7 +1231,7 @@ class ModelAdmin(BaseModelAdmin):
            else:
                form_validated = False
                new_object = self.model()
            formsets = self._create_formsets(request, new_object, inline_instances)
            formsets, inline_instances = self._create_formsets(request, new_object)
            if all_valid(formsets) and form_validated:
                self.save_model(request, new_object, form, False)
                self.save_related(request, form, formsets, False)
@@ -1213,7 +1249,7 @@ class ModelAdmin(BaseModelAdmin):
                if isinstance(f, models.ManyToManyField):
                    initial[k] = initial[k].split(",")
            form = ModelForm(initial=initial)
            formsets = self._create_formsets(request, self.model(), inline_instances)
            formsets, inline_instances = self._create_formsets(request, self.model())

        adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
            self.get_prepopulated_fields(request),
@@ -1266,7 +1302,6 @@ class ModelAdmin(BaseModelAdmin):
                                    current_app=self.admin_site.name))

        ModelForm = self.get_form(request, obj)
        inline_instances = self.get_inline_instances(request, obj)
        if request.method == 'POST':
            form = ModelForm(request.POST, request.FILES, instance=obj)
            if form.is_valid():
@@ -1275,7 +1310,7 @@ class ModelAdmin(BaseModelAdmin):
            else:
                form_validated = False
                new_object = obj
            formsets = self._create_formsets(request, new_object, inline_instances)
            formsets, inline_instances = self._create_formsets(request, new_object)
            if all_valid(formsets) and form_validated:
                self.save_model(request, new_object, form, True)
                self.save_related(request, form, formsets, True)
@@ -1285,7 +1320,7 @@ class ModelAdmin(BaseModelAdmin):

        else:
            form = ModelForm(instance=obj)
            formsets = self._create_formsets(request, obj, inline_instances)
            formsets, inline_instances = self._create_formsets(request, obj)

        adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj),
            self.get_prepopulated_fields(request, obj),
@@ -1566,14 +1601,15 @@ class ModelAdmin(BaseModelAdmin):
            "admin/object_history.html"
        ], context, current_app=self.admin_site.name)

    def _create_formsets(self, request, obj, inline_instances):
    def _create_formsets(self, request, obj):
        "Helper function to generate formsets for add/change_view."
        formsets = []
        inline_instances = []
        prefixes = {}
        get_formsets_args = [request]
        if obj.pk:
            get_formsets_args.append(obj)
        for FormSet, inline in zip(self.get_formsets(*get_formsets_args), inline_instances):
        for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args):
            prefix = FormSet.get_default_prefix()
            prefixes[prefix] = prefixes.get(prefix, 0) + 1
            if prefixes[prefix] != 1 or not prefix:
@@ -1590,7 +1626,8 @@ class ModelAdmin(BaseModelAdmin):
                    'save_as_new': '_saveasnew' in request.POST
                })
            formsets.append(FormSet(**formset_params))
        return formsets
            inline_instances.append(inline)
        return formsets, inline_instances


class InlineModelAdmin(BaseModelAdmin):
+2 −0
Original line number Diff line number Diff line
@@ -453,6 +453,8 @@ these changes.
  * ``django.db.backends.util``
  * ``django.forms.util``

* ``ModelAdmin.get_formsets`` will be removed.

2.0
---

+23 −2
Original line number Diff line number Diff line
@@ -454,7 +454,7 @@ subclass::
.. attribute:: ModelAdmin.inlines

    See :class:`InlineModelAdmin` objects below as well as
    :meth:`ModelAdmin.get_formsets`.
    :meth:`ModelAdmin.get_formsets_with_inlines`.

.. attribute:: ModelAdmin.list_display

@@ -1365,6 +1365,9 @@ templates used by the :class:`ModelAdmin` views:

.. method:: ModelAdmin.get_formsets(self, request, obj=None)

    .. deprecated:: 1.7
        Use :meth:`get_formsets_with_inlines()` instead.

    Yields  :class:`InlineModelAdmin`\s for use in admin add and change views.

    For example if you wanted to display a particular inline only in the change
@@ -1380,6 +1383,24 @@ templates used by the :class:`ModelAdmin` views:
                        continue
                    yield inline.get_formset(request, obj)

.. method:: ModelAdmin.get_formsets_with_inlines(self, request, obj=None)

    Yields (``FormSet``, :class:`InlineModelAdmin`) pairs for use in admin add
    and change views.

    For example if you wanted to display a particular inline only in the change
    view, you could override ``get_formsets_with_inlines`` as follows::

        class MyModelAdmin(admin.ModelAdmin):
            inlines = [MyInline, SomeOtherInline]

            def get_formsets_with_inlines(self, request, obj=None):
                for inline in self.get_inline_instances(request, obj):
                    # hide MyInline in the add view
                    if isinstance(inline, MyInline) and obj is None:
                        continue
                    yield inline.get_formset(request, obj), inline

.. method:: ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)

    The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
+13 −6
Original line number Diff line number Diff line
@@ -466,13 +466,13 @@ than simply ``myapp/models.py``, Django would look for :ref:`initial SQL data
will search ``myapp/sql/`` as documented. The old location will continue to
work until Django 1.9.

``declared_fieldsets`` attribute on ``ModelAdmin.``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``declared_fieldsets`` attribute on ``ModelAdmin``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``ModelAdmin.declared_fieldsets`` was deprecated. Despite being a private API,
it will go through a regular deprecation path. This attribute was mostly used
by methods that bypassed ``ModelAdmin.get_fieldsets()`` but this was considered
a bug and has been addressed.
``ModelAdmin.declared_fieldsets`` has been deprecated. Despite being a private
API, it will go through a regular deprecation path. This attribute was mostly
used by methods that bypassed ``ModelAdmin.get_fieldsets()`` but this was
considered a bug and has been addressed.

``syncdb``
~~~~~~~~~~
@@ -491,3 +491,10 @@ to ``utils.py`` in an effort to unify all util and utils references:
* ``django.contrib.gis.db.backends.util``
* ``django.db.backends.util``
* ``django.forms.util``

``get_formsets`` method on ``ModelAdmin``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``ModelAdmin.get_formsets`` has been deprecated in favor of the new
:meth:`~django.contrib.admin.ModelAdmin.get_formsets_with_inlines`, in order to
better handle the case of selecting showing inlines on a ``ModelAdmin``.
+90 −3
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import warnings

from django.conf import settings
from django.contrib import admin
@@ -277,7 +278,7 @@ class GenericInlineModelAdminTest(TestCase):

        ma = EpisodeAdmin(Episode, self.site)
        self.assertEqual(
            list(list(ma.get_formsets(request))[0]().forms[0].fields),
            list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
            ['keywords', 'id', 'DELETE'])

    def test_custom_form_meta_exclude(self):
@@ -307,7 +308,7 @@ class GenericInlineModelAdminTest(TestCase):

        ma = EpisodeAdmin(Episode, self.site)
        self.assertEqual(
            list(list(ma.get_formsets(request))[0]().forms[0].fields),
            list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
            ['url', 'keywords', 'id', 'DELETE'])

        # Then, only with `ModelForm`  -----------------
@@ -323,7 +324,7 @@ class GenericInlineModelAdminTest(TestCase):

        ma = EpisodeAdmin(Episode, self.site)
        self.assertEqual(
            list(list(ma.get_formsets(request))[0]().forms[0].fields),
            list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
            ['description', 'keywords', 'id', 'DELETE'])

    def test_get_fieldsets(self):
@@ -345,3 +346,89 @@ class GenericInlineModelAdminTest(TestCase):
        ma = MediaInline(Media, self.site)
        form = ma.get_formset(None).form
        self.assertEqual(form._meta.fields, ['url', 'description'])

    def test_get_formsets_with_inlines(self):
        """
        get_formsets() triggers a deprecation warning when get_formsets is
        overridden.
        """
        class MediaForm(ModelForm):
            class Meta:
                model = Media
                exclude = ['url']

        class MediaInline(GenericTabularInline):
            exclude = ['description']
            form = MediaForm
            model = Media

        class EpisodeAdmin(admin.ModelAdmin):
            inlines = [
                MediaInline
            ]

            def get_formsets(self, request, obj=None):
                return []

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            ma = EpisodeAdmin(Episode, self.site)
            list(ma.get_formsets_with_inlines(request))
            # Verify that the deprecation warning was triggered when get_formsets was called
            # This verifies that we called that method.
            self.assertEqual(len(w), 1)
            self.assertTrue(issubclass(w[0].category, PendingDeprecationWarning))

        class EpisodeAdmin(admin.ModelAdmin):
            inlines = [
                MediaInline
            ]
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            ma = EpisodeAdmin(Episode, self.site)
            list(ma.get_formsets_with_inlines(request))
            self.assertEqual(len(w), 0)

    def test_get_formsets_with_inlines_returns_tuples(self):
        """
        Ensure that get_formsets_with_inlines() returns the correct tuples.
        """
        class MediaForm(ModelForm):
            class Meta:
                model = Media
                exclude = ['url']

        class MediaInline(GenericTabularInline):
            form = MediaForm
            model = Media

        class AlternateInline(GenericTabularInline):
            form = MediaForm
            model = Media

        class EpisodeAdmin(admin.ModelAdmin):
            inlines = [
                AlternateInline, MediaInline
            ]
        ma = EpisodeAdmin(Episode, self.site)
        inlines = ma.get_inline_instances(request)
        for (formset, inline), other_inline in zip(ma.get_formsets_with_inlines(request), inlines):
            self.assertIsInstance(formset, other_inline.get_formset(request).__class__)

        class EpisodeAdmin(admin.ModelAdmin):
            inlines = [
                AlternateInline, MediaInline
            ]

            def get_formsets(self, request, obj=None):
                # Catch the deprecation warning to force the usage of get_formsets
                with warnings.catch_warnings(record=True) as w:
                    warnings.simplefilter("always")
                    return super(EpisodeAdmin, self).get_formsets(request, obj)

        ma = EpisodeAdmin(Episode, self.site)
        inlines = ma.get_inline_instances(request)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            for (formset, inline), other_inline in zip(ma.get_formsets_with_inlines(request), inlines):
                self.assertIsInstance(formset, other_inline.get_formset(request).__class__)
Loading