Skip to content

Commit 89ae32a

Browse files
authored
Merge pull request #87 from SmileyChris/fix-streaming-response
Fix HttpResponseBase subclass support in string_view wrapper
2 parents 191da05 + f46eb74 commit 89ae32a

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-3
lines changed

nanodjango/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import inspect
22
from functools import wraps
33

4-
from django.http import HttpResponse
4+
from django.http import HttpResponse, HttpResponseBase
55

66

77
def string_view(fn):
@@ -14,7 +14,7 @@ def string_view(fn):
1414
@wraps(fn)
1515
async def django_view(request, *args, **kwargs):
1616
response = await fn(request, *args, **kwargs)
17-
if isinstance(response, HttpResponse):
17+
if isinstance(response, HttpResponseBase):
1818
return response
1919
return HttpResponse(response)
2020

@@ -23,7 +23,7 @@ async def django_view(request, *args, **kwargs):
2323
@wraps(fn)
2424
def django_view(request, *args, **kwargs):
2525
response = fn(request, *args, **kwargs)
26-
if isinstance(response, HttpResponse):
26+
if isinstance(response, HttpResponseBase):
2727
return response
2828
return HttpResponse(response)
2929

tests/test_views.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import json
2+
from io import BytesIO
3+
4+
from django.http import FileResponse, HttpResponse, JsonResponse, StreamingHttpResponse
5+
6+
17
def test_get(client):
28
response = client.get("/")
39
assert response.status_code == 200
@@ -14,3 +20,86 @@ def test_get_with_re_param(client):
1420
response = client.get("/a/")
1521
assert response.status_code == 200
1622
assert response.content == b"Hello a"
23+
24+
25+
def test_streaming_response(nanodjango_app, client):
26+
"""Test that StreamingHttpResponse works with @app.route decorator"""
27+
28+
def event_generator():
29+
for i in range(3):
30+
yield f"data: {json.dumps({'count': i})}\n\n".encode("utf-8")
31+
32+
@nanodjango_app.route("/stream")
33+
def stream_view(request):
34+
return StreamingHttpResponse(
35+
event_generator(), content_type="text/event-stream"
36+
)
37+
38+
response = client.get("/stream")
39+
assert response.status_code == 200
40+
assert response["Content-Type"] == "text/event-stream"
41+
assert response.streaming
42+
43+
# Collect all chunks
44+
content = b"".join(response.streaming_content)
45+
assert b"data: " in content
46+
assert b'"count": 0' in content
47+
assert b'"count": 1' in content
48+
assert b'"count": 2' in content
49+
50+
51+
def test_http_response_unchanged(nanodjango_app, client):
52+
"""Test that regular HttpResponse still works"""
53+
54+
@nanodjango_app.route("/http-response")
55+
def http_view(request):
56+
return HttpResponse("Direct HttpResponse", content_type="text/plain")
57+
58+
response = client.get("/http-response")
59+
assert response.status_code == 200
60+
assert response.content == b"Direct HttpResponse"
61+
assert response["Content-Type"] == "text/plain"
62+
63+
64+
def test_json_response_unchanged(nanodjango_app, client):
65+
"""Test that JsonResponse still works"""
66+
67+
@nanodjango_app.route("/json-response")
68+
def json_view(request):
69+
return JsonResponse({"message": "test"})
70+
71+
response = client.get("/json-response")
72+
assert response.status_code == 200
73+
assert response["Content-Type"] == "application/json"
74+
data = json.loads(response.content)
75+
assert data["message"] == "test"
76+
77+
78+
def test_string_conversion_still_works(nanodjango_app, client):
79+
"""Test that string return values are still converted to HttpResponse"""
80+
81+
@nanodjango_app.route("/string-return")
82+
def string_view(request):
83+
return "Plain string"
84+
85+
response = client.get("/string-return")
86+
assert response.status_code == 200
87+
assert response.content == b"Plain string"
88+
89+
90+
def test_file_response(nanodjango_app, client):
91+
"""Test that FileResponse works with @app.route decorator"""
92+
93+
@nanodjango_app.route("/file")
94+
def file_view(request):
95+
file_content = b"This is file content"
96+
file_obj = BytesIO(file_content)
97+
return FileResponse(file_obj, content_type="text/plain", as_attachment=False)
98+
99+
response = client.get("/file")
100+
assert response.status_code == 200
101+
assert response["Content-Type"] == "text/plain"
102+
# FileResponse is a StreamingHttpResponse
103+
assert response.streaming
104+
content = b"".join(response.streaming_content)
105+
assert content == b"This is file content"

0 commit comments

Comments
 (0)