mirror of
https://github.com/django/django.git
synced 2024-12-23 09:36:06 +00:00
Fixed #20331 -- Allowed admin actions to serve StreamingHttpResponses
Thanks Edwin.
This commit is contained in:
parent
8010289ea2
commit
d727518ad6
@ -24,6 +24,7 @@ from django.db.models.related import RelatedObject
|
|||||||
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
|
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
|
||||||
from django.db.models.sql.constants import QUERY_TERMS
|
from django.db.models.sql.constants import QUERY_TERMS
|
||||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||||
|
from django.http.response import HttpResponseBase
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template.response import SimpleTemplateResponse, TemplateResponse
|
from django.template.response import SimpleTemplateResponse, TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@ -1026,10 +1027,10 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
|
|
||||||
response = func(self, request, queryset)
|
response = func(self, request, queryset)
|
||||||
|
|
||||||
# Actions may return an HttpResponse, which will be used as the
|
# Actions may return an HttpResponse-like object, which will be
|
||||||
# response from the POST. If not, we'll be a good little HTTP
|
# used as the response from the POST. If not, we'll be a good
|
||||||
# citizen and redirect back to the changelist page.
|
# little HTTP citizen and redirect back to the changelist page.
|
||||||
if isinstance(response, HttpResponse):
|
if isinstance(response, HttpResponseBase):
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
@ -9,11 +9,13 @@ from django.contrib import admin
|
|||||||
from django.contrib.admin.views.main import ChangeList
|
from django.contrib.admin.views.main import ChangeList
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
|
from django.core.servers.basehttp import FileWrapper
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms.models import BaseModelFormSet
|
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.contrib.admin import BooleanFieldListFilter
|
||||||
|
from django.utils.six import StringIO
|
||||||
|
|
||||||
from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
|
from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
|
||||||
Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link,
|
Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link,
|
||||||
@ -238,8 +240,20 @@ def redirect_to(modeladmin, request, selected):
|
|||||||
redirect_to.short_description = 'Redirect to (Awesome action)'
|
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):
|
class ExternalSubscriberAdmin(admin.ModelAdmin):
|
||||||
actions = [redirect_to, external_mail]
|
actions = [redirect_to, external_mail, download, no_perm]
|
||||||
|
|
||||||
|
|
||||||
class Podcast(Media):
|
class Podcast(Media):
|
||||||
|
@ -2432,6 +2432,29 @@ class AdminActionsTest(TestCase):
|
|||||||
response = self.client.post(url, action_data)
|
response = self.client.post(url, action_data)
|
||||||
self.assertRedirects(response, url)
|
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):
|
def test_actions_ordering(self):
|
||||||
"""
|
"""
|
||||||
Ensure that actions are ordered as expected.
|
Ensure that actions are ordered as expected.
|
||||||
@ -2440,9 +2463,13 @@ class AdminActionsTest(TestCase):
|
|||||||
response = self.client.get('/test_admin/admin/admin_views/externalsubscriber/')
|
response = self.client.get('/test_admin/admin/admin_views/externalsubscriber/')
|
||||||
self.assertContains(response, '''<label>Action: <select name="action">
|
self.assertContains(response, '''<label>Action: <select name="action">
|
||||||
<option value="" selected="selected">---------</option>
|
<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="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)
|
</select>''', html=True)
|
||||||
|
|
||||||
def test_model_without_action(self):
|
def test_model_without_action(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user