From 1821d02f4ab8b3633adf8dfc0dc50dcc6b5b9013 Mon Sep 17 00:00:00 2001 From: Jesse London Date: Tue, 16 Feb 2021 13:47:33 -0600 Subject: [PATCH] made Browsable API base template cachable: omit CSRF token when unnecessary HTML responses generated by the Browsable API otherwise generate inconsistent ETAGs -- due to the presence of CSRF tokens in the response -- even when the API is read-only, (and as such when the response contains no resource-modifying forms, i.e. neither POST nor PUT forms, which might require the CSRF token). While the template was appropriately including CSRF tokens only within POST and PUT forms, its AJAX overlay included the CSRF token in *every* response, regardless of whether it would be needed. This change brings the logic of the `script` block into line with that of the rest of the template -- and such that read-only APIs (and really the Browsable API pages of *any* read-only resources) will not needlessly include the CSRF token, and will now be safely cachable -- by both back-end systems and by the user agent. --- .../templates/rest_framework/base.html | 2 +- tests/test_templates.py | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index a88e1591c..4d057b632 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -290,7 +290,7 @@ diff --git a/tests/test_templates.py b/tests/test_templates.py index 0dba78ea2..195296e16 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -3,15 +3,23 @@ import re from django.shortcuts import render -def test_base_template_with_context(): - context = {'request': True, 'csrf_token': 'TOKEN'} - result = render({}, 'rest_framework/base.html', context=context) - assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode()) - - def test_base_template_with_no_context(): # base.html should be renderable with no context, # so it can be easily extended. result = render({}, 'rest_framework/base.html') # note that this response will not include a valid CSRF token assert re.search(r'\bcsrfToken: ""', result.content.decode()) + + +def test_base_template_with_simple_context(): + context = {'request': True, 'csrf_token': 'TOKEN'} + result = render({}, 'rest_framework/base.html', context=context) + # note that response will STILL not include a CSRF token + assert re.search(r'\bcsrfToken: ""', result.content.decode()) + + +def test_base_template_with_editing_context(): + context = {'request': True, 'post_form': object(), 'csrf_token': 'TOKEN'} + result = render({}, 'rest_framework/base.html', context=context) + # response includes a CSRF token in support of the POST form + assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode())