Closes #22687: Fix queryset truthiness check in RenderTemplateMixin.render_to_response() (#22689)

This commit is contained in:
bctiemann 2026-07-16 15:17:21 -04:00 committed by GitHub
parent 6c501413ee
commit f0a58362f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 1 deletions

View File

@ -244,7 +244,7 @@ class RenderTemplateMixin(models.Model):
extension = f'.{self.file_extension}' if self.file_extension else ''
if self.file_name:
filename = self.file_name
elif queryset:
elif queryset is not None:
filename = filename_from_model(queryset.model)
elif context:
filename = filename_from_object(context)

View File

@ -11,8 +11,10 @@ from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.core.files.storage import Storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import connection
from django.forms import ValidationError
from django.test import TestCase, tag
from django.test.utils import CaptureQueriesContext
from jinja2 import DebugUndefined, StrictUndefined, TemplateError, TemplateSyntaxError, UndefinedError
from PIL import Image
@ -1446,6 +1448,37 @@ class RenderTemplateMixinResponseTestCase(TestCase):
response = t.render_to_response(queryset=Site.objects.all())
self.assertEqual(response['Content-Disposition'], 'attachment; filename="netbox_sites.txt"')
def test_response_attachment_filename_from_empty_queryset(self):
"""An empty (but non-None) queryset must still yield a model-derived filename."""
t = ExportTemplate(
name='t',
template_code='{% for obj in queryset %}{{ obj.name }}{% endfor %}',
file_extension='txt',
as_attachment=True,
)
response = t.render_to_response(queryset=Site.objects.none())
self.assertEqual(response['Content-Disposition'], 'attachment; filename="netbox_sites.txt"')
def test_response_attachment_does_not_force_queryset_evaluation(self):
"""A template that never references `queryset` must not force it to be evaluated."""
Site.objects.bulk_create([Site(name=f'Site {i}', slug=f'site-{i}') for i in range(5)])
t = ExportTemplate(
name='t',
template_code='static output', # deliberately does not reference `queryset`
file_extension='txt',
as_attachment=True,
)
with CaptureQueriesContext(connection) as ctx:
t.render_to_response(queryset=Site.objects.all())
table = Site._meta.db_table
site_queries = [q for q in ctx.captured_queries if table in q['sql']]
self.assertEqual(
site_queries, [],
f"render_to_response() queried {table} even though the template never "
f"references `queryset`:\n{site_queries}"
)
def test_response_attachment_filename_from_device_context(self):
t = ConfigTemplate(name='t', template_code='ok', as_attachment=True)
device = SimpleNamespace(name='router1')