Skip to content

Commit

Permalink
Fixed #20331 -- Allowed admin actions to serve StreamingHttpResponses
Browse files Browse the repository at this point in the history
Thanks Edwin.
  • Loading branch information
timgraham committed May 29, 2013
1 parent 8010289 commit d727518
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 8 deletions.
9 changes: 5 additions & 4 deletions django/contrib/admin/options.py
Expand Up @@ -24,6 +24,7 @@
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
from django.db.models.sql.constants import QUERY_TERMS
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.http.response import HttpResponseBase
from django.shortcuts import get_object_or_404
from django.template.response import SimpleTemplateResponse, TemplateResponse
from django.utils.decorators import method_decorator
Expand Down Expand Up @@ -1026,10 +1027,10 @@ def response_action(self, request, queryset):

response = func(self, request, queryset)

# Actions may return an HttpResponse, which will be used as the
# response from the POST. If not, we'll be a good little HTTP
# citizen and redirect back to the changelist page.
if isinstance(response, HttpResponse):
# Actions may return an HttpResponse-like object, which will be
# used as the response from the POST. If not, we'll be a good
# little HTTP citizen and redirect back to the changelist page.
if isinstance(response, HttpResponseBase):
return response
else:
return HttpResponseRedirect(request.get_full_path())
Expand Down
18 changes: 16 additions & 2 deletions tests/admin_views/admin.py
Expand Up @@ -9,11 +9,13 @@
from django.contrib.admin.views.main import ChangeList
from django.core.files.storage import FileSystemStorage
from django.core.mail import EmailMessage
from django.core.servers.basehttp import FileWrapper
from django.conf.urls import patterns, url
from django.db import models
from django.forms.models import BaseModelFormSet
from django.http import HttpResponse
from django.http import HttpResponse, StreamingHttpResponse
from django.contrib.admin import BooleanFieldListFilter
from django.utils.six import StringIO

from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link,
Expand Down Expand Up @@ -238,8 +240,20 @@ def redirect_to(modeladmin, request, selected):
redirect_to.short_description = 'Redirect to (Awesome action)'


def download(modeladmin, request, selected):
buf = StringIO('This is the content of the file')
return StreamingHttpResponse(FileWrapper(buf))
download.short_description = 'Download subscription'


def no_perm(modeladmin, request, selected):
return HttpResponse(content='No permission to perform this action',
status=403)
no_perm.short_description = 'No permission to run'


class ExternalSubscriberAdmin(admin.ModelAdmin):
actions = [redirect_to, external_mail]
actions = [redirect_to, external_mail, download, no_perm]


class Podcast(Media):
Expand Down
31 changes: 29 additions & 2 deletions tests/admin_views/tests.py
Expand Up @@ -2432,6 +2432,29 @@ def test_default_redirect(self):
response = self.client.post(url, action_data)
self.assertRedirects(response, url)

def test_custom_function_action_streaming_response(self):
"""Tests a custom action that returns a StreamingHttpResponse."""
action_data = {
ACTION_CHECKBOX_NAME: [1],
'action': 'download',
'index': 0,
}
response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
content = b''.join(response.streaming_content)
self.assertEqual(content, b'This is the content of the file')
self.assertEqual(response.status_code, 200)

def test_custom_function_action_no_perm_response(self):
"""Tests a custom action that returns an HttpResponse with 403 code."""
action_data = {
ACTION_CHECKBOX_NAME: [1],
'action': 'no_perm',
'index': 0,
}
response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.content, b'No permission to perform this action')

def test_actions_ordering(self):
"""
Ensure that actions are ordered as expected.
Expand All @@ -2440,9 +2463,13 @@ def test_actions_ordering(self):
response = self.client.get('/test_admin/admin/admin_views/externalsubscriber/')
self.assertContains(response, '''<label>Action: <select name="action">
<option value="" selected="selected">---------</option>
<option value="delete_selected">Delete selected external subscribers</option>
<option value="delete_selected">Delete selected external
subscribers</option>
<option value="redirect_to">Redirect to (Awesome action)</option>
<option value="external_mail">External mail (Another awesome action)</option>
<option value="external_mail">External mail (Another awesome
action)</option>
<option value="download">Download subscription</option>
<option value="no_perm">No permission to run</option>
</select>''', html=True)

def test_model_without_action(self):
Expand Down

0 comments on commit d727518

Please sign in to comment.