Skip to content

Commit 701d83f

Browse files
authored
Merge pull request #425 from PaleNeutron/fix-no-cache
fix #424, no-cache should store the result to cache
2 parents 00d34e4 + 8b5601c commit 701d83f

File tree

4 files changed

+42
-19
lines changed

4 files changed

+42
-19
lines changed

examples/in_memory/main.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ async def uncached_put():
114114
put_ret = put_ret + 1
115115
return {"value": put_ret}
116116

117+
put_ret2 = 0
118+
119+
@app.get("/cached_put")
120+
@cache(namespace="test", expire=5)
121+
async def cached_put():
122+
global put_ret2
123+
put_ret2 = put_ret2 + 1
124+
return {"value": put_ret2}
125+
117126

118127
@app.get("/namespaced_injection")
119128
@cache(namespace="test", expire=5, injected_dependency_namespace="monty_python")

fastapi_cache/coder.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515

1616
import pendulum
1717
from fastapi.encoders import jsonable_encoder
18-
from pydantic import BaseConfig, ValidationError, fields
1918
from starlette.responses import JSONResponse
2019
from starlette.templating import (
2120
_TemplateResponse as TemplateResponse, # pyright: ignore[reportPrivateUsage]
2221
)
2322

23+
24+
class ModelField:
25+
pass
26+
2427
_T = TypeVar("_T", bound=type)
2528

2629

@@ -69,7 +72,7 @@ def decode(cls, value: bytes) -> Any:
6972
# decode_as_type method and then stores a different kind of field for a
7073
# given type, do make sure that the subclass provides its own class
7174
# attribute for this cache.
72-
_type_field_cache: ClassVar[Dict[Any, fields.ModelField]] = {}
75+
_type_field_cache: ClassVar[Dict[Any, ModelField]] = {}
7376

7477
@overload
7578
@classmethod
@@ -89,18 +92,6 @@ def decode_as_type(cls, value: bytes, *, type_: Optional[_T]) -> Union[_T, Any]:
8992
9093
"""
9194
result = cls.decode(value)
92-
if type_ is not None:
93-
try:
94-
field = cls._type_field_cache[type_]
95-
except KeyError:
96-
field = cls._type_field_cache[type_] = fields.ModelField(
97-
name="body", type_=type_, class_validators=None, model_config=BaseConfig
98-
)
99-
result, errors = field.validate(result, {}, loc=())
100-
if errors is not None:
101-
if not isinstance(errors, list):
102-
errors = [errors]
103-
raise ValidationError(errors, type_)
10495
return result
10596

10697

fastapi_cache/decorator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def _uncacheable(request: Optional[Request]) -> bool:
7272
Returns true if:
7373
- Caching has been disabled globally
7474
- This is not a GET request
75-
- The request has a Cache-Control header with a value of "no-store" or "no-cache"
75+
- The request has a Cache-Control header with a value of "no-store"
7676
7777
"""
7878
if not FastAPICache.get_enable():
@@ -81,7 +81,7 @@ def _uncacheable(request: Optional[Request]) -> bool:
8181
return False
8282
if request.method != "GET":
8383
return True
84-
return request.headers.get("Cache-Control") in ("no-store", "no-cache")
84+
return request.headers.get("Cache-Control") == "no-store"
8585

8686

8787
def cache(
@@ -182,7 +182,7 @@ async def ensure_async_func(*args: P.args, **kwargs: P.kwargs) -> R:
182182
)
183183
ttl, cached = 0, None
184184

185-
if cached is None: # cache miss
185+
if cached is None or (request is not None and request.headers.get("Cache-Control") == "no-cache") : # cache miss
186186
result = await ensure_async_func(*args, **kwargs)
187187
to_cache = coder.encode(result)
188188

tests/test_decorator.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ def test_pydantic_model() -> None:
9999

100100
def test_non_get() -> None:
101101
with TestClient(app) as client:
102-
response = client.put("/uncached_put")
102+
response = client.put("/cached_put")
103103
assert "X-FastAPI-Cache" not in response.headers
104104
assert response.json() == {"value": 1}
105-
response = client.put("/uncached_put")
105+
response = client.put("/cached_put")
106106
assert "X-FastAPI-Cache" not in response.headers
107107
assert response.json() == {"value": 2}
108108

@@ -112,3 +112,26 @@ def test_alternate_injected_namespace() -> None:
112112
response = client.get("/namespaced_injection")
113113
assert response.headers.get("X-FastAPI-Cache") == "MISS"
114114
assert response.json() == {"__fastapi_cache_request": 42, "__fastapi_cache_response": 17}
115+
116+
def test_cache_control() -> None:
117+
with TestClient(app) as client:
118+
response = client.get("/cached_put")
119+
assert response.json() == {"value": 1}
120+
121+
# HIT
122+
response = client.get("/cached_put")
123+
assert response.json() == {"value": 1}
124+
125+
# no-cache
126+
response = client.get("/cached_put", headers={"Cache-Control": "no-cache"})
127+
assert response.json() == {"value": 2}
128+
129+
response = client.get("/cached_put")
130+
assert response.json() == {"value": 2}
131+
132+
# no-store
133+
response = client.get("/cached_put", headers={"Cache-Control": "no-store"})
134+
assert response.json() == {"value": 3}
135+
136+
response = client.get("/cached_put")
137+
assert response.json() == {"value": 2}

0 commit comments

Comments
 (0)