mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-04-25 00:54:52 +00:00
Introduce make_preview_request method to supersede dummy_request
Django-1.10-style middleware inherently doesn't support applying middleware to the request independently of running the view function, as the design of dummy_request requires. The current implementation of dummy_request unwittingly works around this by running the entire request/response cycle on the page's live URL (regardless of whether the page actually exists there at that moment), throwing away the response, and returning the request object to be used a second time (at which point it will be hopefully be populated with middleware-supplied attributes such as request.user and request.site - unless something caused the middleware to abort). The new make_preview_request method wraps the call to serve_preview inside the middleware processing, so there is no longer a bogus 'background' request and no response has to be thrown away (meaning that any response returned by middleware will be correctly returned). Fixes #3546
This commit is contained in:
parent
1d9e0acfb8
commit
e400d8d0c8
4 changed files with 58 additions and 14 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
from itertools import chain
|
||||
from unittest import mock
|
||||
|
||||
|
|
@ -5276,7 +5275,6 @@ class TestDraftAccess(TestCase, WagtailTestUtils):
|
|||
# User can view
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@unittest.expectedFailure
|
||||
def test_middleware_response_is_returned(self):
|
||||
"""
|
||||
If middleware returns a response while serving a page preview, that response should be
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ def view_draft(request, page_id):
|
|||
perms = page.permissions_for_user(request.user)
|
||||
if not (perms.can_publish() or perms.can_edit()):
|
||||
raise PermissionDenied
|
||||
return page.serve_preview(page.dummy_request(request), page.default_preview_mode)
|
||||
return page.make_preview_request(request, page.default_preview_mode)
|
||||
|
||||
|
||||
class PreviewOnEdit(View):
|
||||
|
|
@ -646,8 +646,7 @@ class PreviewOnEdit(View):
|
|||
|
||||
form.save(commit=False)
|
||||
preview_mode = request.GET.get('mode', page.default_preview_mode)
|
||||
return page.serve_preview(page.dummy_request(request),
|
||||
preview_mode)
|
||||
return page.make_preview_request(request, preview_mode)
|
||||
|
||||
|
||||
class PreviewOnCreate(PreviewOnEdit):
|
||||
|
|
@ -1055,11 +1054,9 @@ def preview_for_moderation(request, revision_id):
|
|||
|
||||
page = revision.as_page_object()
|
||||
|
||||
request.revision_id = revision_id
|
||||
|
||||
# pass in the real user request rather than page.dummy_request(), so that request.user
|
||||
# and request.revision_id will be picked up by the wagtail user bar
|
||||
return page.serve_preview(request, page.default_preview_mode)
|
||||
return page.make_preview_request(request, page.default_preview_mode, extra_request_attrs={
|
||||
'revision_id': revision_id
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
|
|
@ -1180,7 +1177,7 @@ def revisions_view(request, page_id, revision_id):
|
|||
revision = get_object_or_404(page.revisions, id=revision_id)
|
||||
revision_page = revision.as_page_object()
|
||||
|
||||
return revision_page.serve_preview(page.dummy_request(request), page.default_preview_mode)
|
||||
return revision_page.make_preview_request(request, page.default_preview_mode)
|
||||
|
||||
|
||||
def revisions_compare(request, page_id, revision_id_a, revision_id_b):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import logging
|
|||
from collections import defaultdict
|
||||
from io import StringIO
|
||||
from urllib.parse import urlparse
|
||||
from warnings import warn
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
|
|
@ -32,6 +33,8 @@ from wagtail.core.sites import get_site_for_hostname
|
|||
from wagtail.core.url_routing import RouteResult
|
||||
from wagtail.core.utils import WAGTAIL_APPEND_SLASH, camelcase_to_underscore, resolve_model_string
|
||||
from wagtail.search import index
|
||||
from wagtail.utils.deprecation import RemovedInWagtail29Warning
|
||||
|
||||
|
||||
logger = logging.getLogger('wagtail.core')
|
||||
|
||||
|
|
@ -1216,16 +1219,51 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
user_perms = UserPagePermissionsProxy(user)
|
||||
return user_perms.for_page(self)
|
||||
|
||||
def dummy_request(self, original_request=None, **meta):
|
||||
def make_preview_request(self, original_request=None, preview_mode=None, extra_request_attrs=None):
|
||||
"""
|
||||
Construct a HttpRequest object that is, as far as possible, representative of ones that would
|
||||
receive this page as a response. Used for previewing / moderation and any other place where we
|
||||
Simulate a request to this page, by constructing a fake HttpRequest object that is (as far
|
||||
as possible) representative of a real request to this page's front-end URL, and invoking
|
||||
serve_preview with that request (and the given preview_mode).
|
||||
|
||||
Used for previewing / moderation and any other place where we
|
||||
want to display a view of this page in the admin interface without going through the regular
|
||||
page routing logic.
|
||||
|
||||
If you pass in a real request object as original_request, additional information (e.g. client IP, cookies)
|
||||
will be included in the dummy request.
|
||||
"""
|
||||
dummy_meta = self._get_dummy_headers(original_request)
|
||||
request = WSGIRequest(dummy_meta)
|
||||
|
||||
# Add a flag to let middleware know that this is a dummy request.
|
||||
request.is_dummy = True
|
||||
|
||||
if extra_request_attrs:
|
||||
for k, v in extra_request_attrs.items():
|
||||
setattr(request, k, v)
|
||||
|
||||
page = self
|
||||
|
||||
# Build a custom django.core.handlers.BaseHandler subclass that invokes serve_preview as
|
||||
# the eventual view function called at the end of the middleware chain, rather than going
|
||||
# through the URL resolver
|
||||
class Handler(BaseHandler):
|
||||
def _get_response(self, request):
|
||||
response = page.serve_preview(request, preview_mode)
|
||||
if hasattr(response, 'render') and callable(response.render):
|
||||
response = response.render()
|
||||
return response
|
||||
|
||||
# Invoke this custom handler.
|
||||
handler = Handler()
|
||||
handler.load_middleware()
|
||||
return handler.get_response(request)
|
||||
|
||||
def _get_dummy_headers(self, original_request=None):
|
||||
"""
|
||||
Return a dict of META information to be included in a faked HttpRequest object to pass to
|
||||
serve_preview.
|
||||
"""
|
||||
url = self.full_url
|
||||
if url:
|
||||
url_info = urlparse(url)
|
||||
|
|
@ -1279,6 +1317,16 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
if header in original_request.META:
|
||||
dummy_values[header] = original_request.META[header]
|
||||
|
||||
return dummy_values
|
||||
|
||||
def dummy_request(self, original_request=None, **meta):
|
||||
warn(
|
||||
"Page.dummy_request is deprecated. Use Page.make_preview_request instead",
|
||||
category=RemovedInWagtail29Warning
|
||||
)
|
||||
|
||||
dummy_values = self._get_dummy_headers(original_request)
|
||||
|
||||
# Add additional custom metadata sent by the caller.
|
||||
dummy_values.update(**meta)
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ PASSWORD_HASHERS = (
|
|||
'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher
|
||||
)
|
||||
|
||||
ALLOWED_HOSTS = ['localhost', 'testserver']
|
||||
|
||||
WAGTAILSEARCH_BACKENDS = {
|
||||
'default': {
|
||||
|
|
|
|||
Loading…
Reference in a new issue