Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleaner approach to HeaderMixin #285

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions braces/views/_other.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.views.decorators.cache import cache_control, never_cache
from django.urls import resolve
from django.utils.encoding import force_str
Expand Down Expand Up @@ -133,27 +134,46 @@ def dispatch(self, request, *args, **kwargs):
return handler(request, *args, **kwargs)


class CustomHeadersTemplateResponse(TemplateResponse):
def __init__(self, *args, **kwargs):
headers = kwargs.pop("headers", {})
headers.update(kwargs.pop("extra_headers", {}))
super().__init__(*args, headers=headers, **kwargs)


class HeaderMixin:
"""
Add extra HTTP headers to a response by specifying them in the
``headers`` attribute or by overriding the ``get_headers()`` method.
"""

headers = {}
response_class = CustomHeadersTemplateResponse

def get_headers(self, request):
return self.headers

def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault("extra_headers", self.get_headers(self.request))
return super().render_to_response(context, **response_kwargs)

def dispatch(self, request, *args, **kwargs):
"""
Override this method to customize the way additional headers are
retrieved. It is mandatory that the returned value supports the
``.items()`` method.
"""
response = super().dispatch(request, *args, **kwargs)
for key, value in self.get_headers(request).items():
if key not in response:
response[key] = value
if not getattr(self, "template_name", None):
# No template so probably no `render_to_response` call
for key, value in self.get_headers(request).items():
if key not in response:
response[key] = value
return response


Expand Down
2 changes: 1 addition & 1 deletion tests/test_access_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ def test_https_does_not_redirect(self):

@pytest.mark.django_db
class TestRecentLoginRequiredMixin(test.TestCase):
""" Scenarios requiring a recent login"""
""" Scenarios requiring a recent login """

view_class = RecentLoginRequiredView
recent_view_url = "/recent_login/"
Expand Down
4 changes: 4 additions & 0 deletions tests/test_other_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,10 @@ def test_existing(self):
response = self.client.get('/headers/existing/')
self.assertEqual(response['X-DJANGO-BRACES-EXISTING'], 'value')

def test_template(self):
response = self.client.get('/headers/template/')
self.assertEqual(response['X-DJANGO-BRACES-1'], '1')

class TestCacheControlMixin(test.TestCase):
"""Scenarios around controlling cache"""
def test_cachecontrol_public(self):
Expand Down
1 change: 1 addition & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
re_path(r'^headers/attribute/$', views.AttributeHeaderView.as_view()),
re_path(r'^headers/method/$', views.MethodHeaderView.as_view()),
re_path(r'^headers/existing/$', views.ExistingHeaderView.as_view()),
re_path(r'^headers/template/$', views.HeadersWithTemplate.as_view()),
# CacheControlMixin tests
re_path(r'^cachecontrol/public/$', views.CacheControlPublicView.as_view()),
# NeverCacheMixin tests
Expand Down
12 changes: 12 additions & 0 deletions tests/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,18 @@ class ExistingHeaderView(views.HeaderMixin, AuxiliaryHeaderView):
'X-DJANGO-BRACES-EXISTING': 'other value'
}

class HeadersWithTemplate(views.SetHeadlineMixin, views.HeaderMixin, TemplateView):
"""
View for testing HeaderMixin with a custom TemplateResponse.
"""

template_name = "blank.html"
headline = "Test headline"

headers = {
"X-DJANGO-BRACES-1": 1
}


class CacheControlPublicView(views.CacheControlMixin, OkView):
"""A public-cached page with a 60 second timeout"""
Expand Down