Skip to content

Commit 2538a99

Browse files
Added Cache-Control header to never cache draft blog posts (#2223)
Fixes #2158. Added `Cache-Control` header to never cache draft blog posts (since we want changes to be reflected immediately). Django's cache middleware will not cache responses when the Cache-Control header is set to no-cache, no-store, or private, which is our case here for draft blog posts. Co-authored-by: Tobias McNulty <[email protected]>
1 parent 9ab060f commit 2538a99

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

blog/tests.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from io import StringIO
44

55
import time_machine
6+
from django.conf import settings
67
from django.contrib.auth.models import Permission, User
78
from django.contrib.contenttypes.models import ContentType
89
from django.core.files.base import ContentFile
910
from django.test import TestCase
11+
from django.test.utils import override_settings
1012
from django.urls import reverse
1113
from django.utils import timezone, translation
1214

@@ -407,6 +409,77 @@ def test_user_cannot_see_unpublished_entries(self):
407409
self.assertEqual(response.status_code, 200)
408410

409411

412+
@override_settings(
413+
# Caching middleware is added in the production settings file;
414+
# simulate that here for the tests.
415+
MIDDLEWARE=(
416+
["django.middleware.cache.UpdateCacheMiddleware"]
417+
+ settings.MIDDLEWARE
418+
+ ["django.middleware.cache.FetchFromCacheMiddleware"]
419+
),
420+
)
421+
class ViewsCachingTestCase(DateTimeMixin, TestCase):
422+
def test_drafts_have_no_cache_headers(self):
423+
"""
424+
Draft (unpublished) entries have no-cache headers.
425+
"""
426+
user = User.objects.create(username="staff", is_staff=True)
427+
content_type = ContentType.objects.get_for_model(Entry)
428+
change_permission = Permission.objects.get(
429+
content_type=content_type, codename="change_entry"
430+
)
431+
user.user_permissions.add(change_permission)
432+
self.client.force_login(user)
433+
434+
unpublished_entry = Entry.objects.create(
435+
pub_date=self.tomorrow,
436+
is_active=True,
437+
headline="unpublished",
438+
slug="unpublished",
439+
)
440+
unpublished_url = reverse(
441+
"weblog:entry",
442+
kwargs={
443+
"year": unpublished_entry.pub_date.year,
444+
"month": unpublished_entry.pub_date.strftime("%b").lower(),
445+
"day": unpublished_entry.pub_date.day,
446+
"slug": unpublished_entry.slug,
447+
},
448+
)
449+
450+
response = self.client.get(unpublished_url)
451+
452+
self.assertEqual(response.status_code, 200)
453+
self.assertIn("Cache-Control", response.headers)
454+
self.assertEqual(
455+
response.headers["Cache-Control"],
456+
"max-age=0, no-cache, no-store, must-revalidate, private",
457+
)
458+
459+
def test_published_blogs_have_cache_control_headers(self):
460+
"""
461+
Published blog posts has Cache-Control header.
462+
"""
463+
entry = Entry.objects.create(
464+
pub_date=self.yesterday,
465+
is_active=True,
466+
headline="published",
467+
slug="published",
468+
)
469+
url = reverse(
470+
"weblog:entry",
471+
kwargs={
472+
"year": entry.pub_date.year,
473+
"month": entry.pub_date.strftime("%b").lower(),
474+
"day": entry.pub_date.day,
475+
"slug": entry.slug,
476+
},
477+
)
478+
response = self.client.get(url)
479+
self.assertEqual(response.status_code, 200)
480+
self.assertEqual(response.headers["Cache-Control"], "max-age=300")
481+
482+
410483
class SitemapTests(DateTimeMixin, TestCase):
411484
def test_sitemap(self):
412485
entry = Entry.objects.create(

blog/views.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from django.utils.cache import add_never_cache_headers
12
from django.views.generic.dates import (
23
ArchiveIndexView,
34
DateDetailView,
@@ -58,3 +59,9 @@ def get_queryset(self):
5859
return Entry.objects.all()
5960
else:
6061
return super().get_queryset()
62+
63+
def get(self, request, *args, **kwargs):
64+
response = super().get(request, *args, **kwargs)
65+
if not self.object.is_published():
66+
add_never_cache_headers(response)
67+
return response

0 commit comments

Comments
 (0)