Fixed #23934 -- Fixed regression in admin views obj parameter.

This commit is contained in:
Kamil Braun 2014-11-27 19:34:14 +01:00 committed by Tim Graham
parent 1da1fe8e16
commit 0623f4dea4
5 changed files with 92 additions and 7 deletions

View File

@ -1425,7 +1425,7 @@ class ModelAdmin(BaseModelAdmin):
else: else:
form_validated = False form_validated = False
new_object = form.instance new_object = form.instance
formsets, inline_instances = self._create_formsets(request, new_object) formsets, inline_instances = self._create_formsets(request, new_object, change=not add)
if all_valid(formsets) and form_validated: if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, not add) self.save_model(request, new_object, form, not add)
self.save_related(request, form, formsets, not add) self.save_related(request, form, formsets, not add)
@ -1440,10 +1440,10 @@ class ModelAdmin(BaseModelAdmin):
if add: if add:
initial = self.get_changeform_initial_data(request) initial = self.get_changeform_initial_data(request)
form = ModelForm(initial=initial) form = ModelForm(initial=initial)
formsets, inline_instances = self._create_formsets(request, self.model()) formsets, inline_instances = self._create_formsets(request, self.model(), change=False)
else: else:
form = ModelForm(instance=obj) form = ModelForm(instance=obj)
formsets, inline_instances = self._create_formsets(request, obj) formsets, inline_instances = self._create_formsets(request, obj, change=True)
adminForm = helpers.AdminForm( adminForm = helpers.AdminForm(
form, form,
@ -1729,13 +1729,13 @@ class ModelAdmin(BaseModelAdmin):
"admin/object_history.html" "admin/object_history.html"
], context, current_app=self.admin_site.name) ], context, current_app=self.admin_site.name)
def _create_formsets(self, request, obj): def _create_formsets(self, request, obj, change):
"Helper function to generate formsets for add/change_view." "Helper function to generate formsets for add/change_view."
formsets = [] formsets = []
inline_instances = [] inline_instances = []
prefixes = {} prefixes = {}
get_formsets_args = [request] get_formsets_args = [request]
if obj.pk: if change:
get_formsets_args.append(obj) get_formsets_args.append(obj)
for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args): for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args):
prefix = FormSet.get_default_prefix() prefix = FormSet.get_default_prefix()

View File

@ -94,3 +94,7 @@ Bugfixes
* Fixed a crash when ``RunSQL`` SQL content was collected by the schema editor, * Fixed a crash when ``RunSQL`` SQL content was collected by the schema editor,
typically when using ``sqlmigrate`` (:ticket:`23909`). typically when using ``sqlmigrate`` (:ticket:`23909`).
* Fixed a regression in ``contrib.admin`` add/change views which caused some
``ModelAdmin`` methods to receive the incorrect ``obj`` value
(:ticket:`23934`).

View File

@ -18,7 +18,8 @@ from django.contrib.admin import BooleanFieldListFilter
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.six import StringIO from django.utils.six import StringIO
from .models import (Article, Chapter, Child, Parent, Picture, Widget, from .models import (
Article, Chapter, Child, Parent, Picture, Widget,
DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link,
PrePopulatedPost, PrePopulatedSubPost, CustomArticle, Section, PrePopulatedPost, PrePopulatedSubPost, CustomArticle, Section,
ModelWithStringPrimaryKey, Color, Thing, Actor, Inquisition, Sketch, ModelWithStringPrimaryKey, Color, Thing, Actor, Inquisition, Sketch,
@ -37,7 +38,9 @@ from .models import (Article, Chapter, Child, Parent, Picture, Widget,
State, City, Restaurant, Worker, ParentWithDependentChildren, State, City, Restaurant, Worker, ParentWithDependentChildren,
DependentChild, StumpJoke, FieldOverridePost, FunkyTag, DependentChild, StumpJoke, FieldOverridePost, FunkyTag,
ReferencedByParent, ChildOfReferer, ReferencedByInline, ReferencedByParent, ChildOfReferer, ReferencedByInline,
InlineReference, InlineReferer, Recipe, Ingredient, NotReferenced) InlineReference, InlineReferer, Recipe, Ingredient, NotReferenced,
ExplicitlyProvidedPK, ImplicitlyGeneratedPK,
)
def callable_year(dt_value): def callable_year(dt_value):
@ -842,6 +845,25 @@ class InlineRefererAdmin(admin.ModelAdmin):
inlines = [InlineReferenceInline] inlines = [InlineReferenceInline]
class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin):
fields = ['name']
def add_view(self, request, *args, **kwargs):
request.is_add_view = True
return super(GetFormsetsArgumentCheckingAdmin, self).add_view(request, *args, **kwargs)
def change_view(self, request, *args, **kwargs):
request.is_add_view = False
return super(GetFormsetsArgumentCheckingAdmin, self).change_view(request, *args, **kwargs)
def get_formsets_with_inlines(self, request, obj=None):
if request.is_add_view and obj is not None:
raise Exception("'obj' passed to get_formsets_with_inlines wasn't None during add_view")
if not request.is_add_view and obj is None:
raise Exception("'obj' passed to get_formsets_with_inlines was None during change_view")
return super(GetFormsetsArgumentCheckingAdmin, self).get_formsets_with_inlines(request, obj)
site = admin.AdminSite(name="admin") site = admin.AdminSite(name="admin")
site.site_url = '/my-site-url/' site.site_url = '/my-site-url/'
site.register(Article, ArticleAdmin) site.register(Article, ArticleAdmin)
@ -942,6 +964,8 @@ site.register(StumpJoke)
site.register(Recipe) site.register(Recipe)
site.register(Ingredient) site.register(Ingredient)
site.register(NotReferenced) site.register(NotReferenced)
site.register(ExplicitlyProvidedPK, GetFormsetsArgumentCheckingAdmin)
site.register(ImplicitlyGeneratedPK, GetFormsetsArgumentCheckingAdmin)
# Register core models we need in our tests # Register core models we need in our tests
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group

View File

@ -874,3 +874,12 @@ class RecipeIngredient(models.Model):
class NotReferenced(models.Model): class NotReferenced(models.Model):
# Don't point any FK at this model. # Don't point any FK at this model.
pass pass
# Models for #23934
class ExplicitlyProvidedPK(models.Model):
name = models.IntegerField(primary_key=True)
class ImplicitlyGeneratedPK(models.Model):
name = models.IntegerField(unique=True)

View File

@ -5207,3 +5207,51 @@ class TestEtagWithAdminView(TestCase):
response = self.client.get('/test_admin/admin/') response = self.client.get('/test_admin/admin/')
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertTrue(response.has_header('ETag')) self.assertTrue(response.has_header('ETag'))
@override_settings(
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
ROOT_URLCONF="admin_views.urls",
)
class GetFormsetsWithInlinesArgumentTest(TestCase):
"""
#23934 - When adding a new model instance in the admin, the 'obj' argument
of get_formsets_with_inlines() should be None. When changing, it should be
equal to the existing model instance.
The GetFormsetsArgumentCheckingAdmin ModelAdmin throws an exception
if obj is not None during add_view or obj is None during change_view.
"""
fixtures = ['admin-views-users.xml']
def setUp(self):
self.client.login(username='super', password='secret')
def test_explicitly_provided_pk(self):
post_data = {'name': '1'}
try:
response = self.client.post('/test_admin/admin/admin_views/explicitlyprovidedpk/add/', post_data)
except Exception as e:
self.fail(e)
self.assertEqual(response.status_code, 302)
post_data = {'name': '2'}
try:
response = self.client.post('/test_admin/admin/admin_views/explicitlyprovidedpk/1/', post_data)
except Exception as e:
self.fail(e)
self.assertEqual(response.status_code, 302)
def test_implicitly_generated_pk(self):
post_data = {'name': '1'}
try:
response = self.client.post('/test_admin/admin/admin_views/implicitlygeneratedpk/add/', post_data)
except Exception as e:
self.fail(e)
self.assertEqual(response.status_code, 302)
post_data = {'name': '2'}
try:
response = self.client.post('/test_admin/admin/admin_views/implicitlygeneratedpk/1/', post_data)
except Exception as e:
self.fail(e)
self.assertEqual(response.status_code, 302)