We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[beta][v20] Lire une notification n'invalide pas le cache
Serveur : Beta
Version : v20-RC2/99bee1d
Système : Mac OS X
Navigateur : 52.0.2743.116 (64-bit)

---
1. Générez une notification.
2. Lisez là depuis le site.
3. Récupérez la liste des notifications par l'API.
4. Si le timeout de 15 minutes n'est pas passé par là, la notification est toujours marquée comme non lue dans la réponse de l'API.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zds/notification/api/views.py`
Content:
```
1 # coding: utf-8
2 from dry_rest_permissions.generics import DRYPermissions
3 from rest_framework import filters
4 from rest_framework.generics import ListAPIView
5 from rest_framework.permissions import IsAuthenticated
6 from rest_framework_extensions.cache.decorators import cache_response
7 from rest_framework_extensions.etag.decorators import etag
8 from rest_framework_extensions.key_constructor import bits
9 from rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor
10 
11 from zds.api.bits import DJRF3xPaginationKeyBit
12 from zds.notification.api.serializers import NotificationSerializer
13 from zds.notification.models import Notification
14 
15 
16 class PagingNotificationListKeyConstructor(DefaultKeyConstructor):
17     pagination = DJRF3xPaginationKeyBit()
18     search = bits.QueryParamsKeyBit(['search', 'ordering', 'type'])
19     list_sql_query = bits.ListSqlQueryKeyBit()
20     unique_view_id = bits.UniqueViewIdKeyBit()
21     user = bits.UserKeyBit()
22 
23 
24 class NotificationListAPI(ListAPIView):
25     """
26     List of notification.
27     """
28 
29     filter_backends = (filters.SearchFilter, filters.OrderingFilter)
30     search_fields = ('title',)
31     ordering_fields = ('pubdate', 'title',)
32     list_key_func = PagingNotificationListKeyConstructor()
33     serializer_class = NotificationSerializer
34     permission_classes = (IsAuthenticated, DRYPermissions,)
35 
36     @etag(list_key_func)
37     @cache_response(key_func=list_key_func)
38     def get(self, request, *args, **kwargs):
39         """
40         Lists all notifications of a user.
41         ---
42 
43         parameters:
44             - name: Authorization
45               description: Bearer token to make an authenticated request.
46               required: true
47               paramType: header
48             - name: page
49               description: Restricts output to the given page number.
50               required: false
51               paramType: query
52             - name: page_size
53               description: Sets the number of notifications per page.
54               required: false
55               paramType: query
56             - name: search
57               description: Filters by title.
58               required: false
59               paramType: query
60             - name: ordering
61               description: Sorts the results. You can order by (-)pubdate or (-)title.
62               paramType: query
63             - name: type
64               description: Filters by notification type.
65               paramType: query
66             - name: subscription_type
67               description: Filters by subscription type.
68               paramType: query
69             - name: expand
70               description: Returns an object instead of an identifier representing the given field.
71               required: false
72               paramType: query
73         responseMessages:
74             - code: 401
75               message: Not Authenticated
76             - code: 404
77               message: Not Found
78         """
79         return self.list(request, *args, **kwargs)
80 
81     def get_queryset(self):
82         queryset = Notification.objects.get_notifications_of(self.request.user)
83         subscription_type = self.request.query_params.get('subscription_type', None)
84         if subscription_type:
85             queryset = queryset.filter(subscription__content_type__model=subscription_type)
86         _type = self.request.query_params.get('type', None)
87         if _type:
88             queryset = queryset.filter(content_type__model=_type)
89         return queryset
90
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zds/notification/api/views.py b/zds/notification/api/views.py
--- a/zds/notification/api/views.py
+++ b/zds/notification/api/views.py
@@ -1,4 +1,8 @@
 # coding: utf-8
+import datetime
+from django.core.cache import cache
+from django.db.models.signals import post_delete
+from django.db.models.signals import post_save
 from dry_rest_permissions.generics import DRYPermissions
 from rest_framework import filters
 from rest_framework.generics import ListAPIView
@@ -8,7 +12,7 @@
 from rest_framework_extensions.key_constructor import bits
 from rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor
 
-from zds.api.bits import DJRF3xPaginationKeyBit
+from zds.api.bits import DJRF3xPaginationKeyBit, UpdatedAtKeyBit
 from zds.notification.api.serializers import NotificationSerializer
 from zds.notification.models import Notification
 
@@ -19,6 +23,15 @@
     list_sql_query = bits.ListSqlQueryKeyBit()
     unique_view_id = bits.UniqueViewIdKeyBit()
     user = bits.UserKeyBit()
+    updated_at = UpdatedAtKeyBit('api_updated_notification')
+
+
+def change_api_notification_updated_at(sender=None, instance=None, *args, **kwargs):
+    cache.set('api_updated_notification', datetime.datetime.utcnow())
+
+
+post_save.connect(receiver=change_api_notification_updated_at, sender=Notification)
+post_delete.connect(receiver=change_api_notification_updated_at, sender=Notification)
 
 
 class NotificationListAPI(ListAPIView):

{"golden_diff": "diff --git a/zds/notification/api/views.py b/zds/notification/api/views.py\n--- a/zds/notification/api/views.py\n+++ b/zds/notification/api/views.py\n@@ -1,4 +1,8 @@\n # coding: utf-8\n+import datetime\n+from django.core.cache import cache\n+from django.db.models.signals import post_delete\n+from django.db.models.signals import post_save\n from dry_rest_permissions.generics import DRYPermissions\n from rest_framework import filters\n from rest_framework.generics import ListAPIView\n@@ -8,7 +12,7 @@\n from rest_framework_extensions.key_constructor import bits\n from rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor\n \n-from zds.api.bits import DJRF3xPaginationKeyBit\n+from zds.api.bits import DJRF3xPaginationKeyBit, UpdatedAtKeyBit\n from zds.notification.api.serializers import NotificationSerializer\n from zds.notification.models import Notification\n \n@@ -19,6 +23,15 @@\n     list_sql_query = bits.ListSqlQueryKeyBit()\n     unique_view_id = bits.UniqueViewIdKeyBit()\n     user = bits.UserKeyBit()\n+    updated_at = UpdatedAtKeyBit('api_updated_notification')\n+\n+\n+def change_api_notification_updated_at(sender=None, instance=None, *args, **kwargs):\n+    cache.set('api_updated_notification', datetime.datetime.utcnow())\n+\n+\n+post_save.connect(receiver=change_api_notification_updated_at, sender=Notification)\n+post_delete.connect(receiver=change_api_notification_updated_at, sender=Notification)\n \n \n class NotificationListAPI(ListAPIView):\n", "issue": "[beta][v20] Lire une notification n'invalide pas le cache\nServeur : Beta\nVersion : v20-RC2/99bee1d\nSyst\u00e8me : Mac OS X\nNavigateur : 52.0.2743.116 (64-bit)\n\n---\n1. G\u00e9n\u00e9rez une notification.\n2. Lisez l\u00e0 depuis le site.\n3. R\u00e9cup\u00e9rez la liste des notifications par l'API.\n4. Si le timeout de 15 minutes n'est pas pass\u00e9 par l\u00e0, la notification est toujours marqu\u00e9e comme non lue dans la r\u00e9ponse de l'API.\n\n", "before_files": [{"content": "# coding: utf-8\nfrom dry_rest_permissions.generics import DRYPermissions\nfrom rest_framework import filters\nfrom rest_framework.generics import ListAPIView\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework_extensions.cache.decorators import cache_response\nfrom rest_framework_extensions.etag.decorators import etag\nfrom rest_framework_extensions.key_constructor import bits\nfrom rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor\n\nfrom zds.api.bits import DJRF3xPaginationKeyBit\nfrom zds.notification.api.serializers import NotificationSerializer\nfrom zds.notification.models import Notification\n\n\nclass PagingNotificationListKeyConstructor(DefaultKeyConstructor):\n    pagination = DJRF3xPaginationKeyBit()\n    search = bits.QueryParamsKeyBit(['search', 'ordering', 'type'])\n    list_sql_query = bits.ListSqlQueryKeyBit()\n    unique_view_id = bits.UniqueViewIdKeyBit()\n    user = bits.UserKeyBit()\n\n\nclass NotificationListAPI(ListAPIView):\n    \"\"\"\n    List of notification.\n    \"\"\"\n\n    filter_backends = (filters.SearchFilter, filters.OrderingFilter)\n    search_fields = ('title',)\n    ordering_fields = ('pubdate', 'title',)\n    list_key_func = PagingNotificationListKeyConstructor()\n    serializer_class = NotificationSerializer\n    permission_classes = (IsAuthenticated, DRYPermissions,)\n\n    @etag(list_key_func)\n    @cache_response(key_func=list_key_func)\n    def get(self, request, *args, **kwargs):\n        \"\"\"\n        Lists all notifications of a user.\n        ---\n\n        parameters:\n            - name: Authorization\n              description: Bearer token to make an authenticated request.\n              required: true\n              paramType: header\n            - name: page\n              description: Restricts output to the given page number.\n              required: false\n              paramType: query\n            - name: page_size\n              description: Sets the number of notifications per page.\n              required: false\n              paramType: query\n            - name: search\n              description: Filters by title.\n              required: false\n              paramType: query\n            - name: ordering\n              description: Sorts the results. You can order by (-)pubdate or (-)title.\n              paramType: query\n            - name: type\n              description: Filters by notification type.\n              paramType: query\n            - name: subscription_type\n              description: Filters by subscription type.\n              paramType: query\n            - name: expand\n              description: Returns an object instead of an identifier representing the given field.\n              required: false\n              paramType: query\n        responseMessages:\n            - code: 401\n              message: Not Authenticated\n            - code: 404\n              message: Not Found\n        \"\"\"\n        return self.list(request, *args, **kwargs)\n\n    def get_queryset(self):\n        queryset = Notification.objects.get_notifications_of(self.request.user)\n        subscription_type = self.request.query_params.get('subscription_type', None)\n        if subscription_type:\n            queryset = queryset.filter(subscription__content_type__model=subscription_type)\n        _type = self.request.query_params.get('type', None)\n        if _type:\n            queryset = queryset.filter(content_type__model=_type)\n        return queryset\n", "path": "zds/notification/api/views.py"}], "after_files": [{"content": "# coding: utf-8\nimport datetime\nfrom django.core.cache import cache\nfrom django.db.models.signals import post_delete\nfrom django.db.models.signals import post_save\nfrom dry_rest_permissions.generics import DRYPermissions\nfrom rest_framework import filters\nfrom rest_framework.generics import ListAPIView\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework_extensions.cache.decorators import cache_response\nfrom rest_framework_extensions.etag.decorators import etag\nfrom rest_framework_extensions.key_constructor import bits\nfrom rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor\n\nfrom zds.api.bits import DJRF3xPaginationKeyBit, UpdatedAtKeyBit\nfrom zds.notification.api.serializers import NotificationSerializer\nfrom zds.notification.models import Notification\n\n\nclass PagingNotificationListKeyConstructor(DefaultKeyConstructor):\n    pagination = DJRF3xPaginationKeyBit()\n    search = bits.QueryParamsKeyBit(['search', 'ordering', 'type'])\n    list_sql_query = bits.ListSqlQueryKeyBit()\n    unique_view_id = bits.UniqueViewIdKeyBit()\n    user = bits.UserKeyBit()\n    updated_at = UpdatedAtKeyBit('api_updated_notification')\n\n\ndef change_api_notification_updated_at(sender=None, instance=None, *args, **kwargs):\n    cache.set('api_updated_notification', datetime.datetime.utcnow())\n\n\npost_save.connect(receiver=change_api_notification_updated_at, sender=Notification)\npost_delete.connect(receiver=change_api_notification_updated_at, sender=Notification)\n\n\nclass NotificationListAPI(ListAPIView):\n    \"\"\"\n    List of notification.\n    \"\"\"\n\n    filter_backends = (filters.SearchFilter, filters.OrderingFilter)\n    search_fields = ('title',)\n    ordering_fields = ('pubdate', 'title',)\n    list_key_func = PagingNotificationListKeyConstructor()\n    serializer_class = NotificationSerializer\n    permission_classes = (IsAuthenticated, DRYPermissions,)\n\n    @etag(list_key_func)\n    @cache_response(key_func=list_key_func)\n    def get(self, request, *args, **kwargs):\n        \"\"\"\n        Lists all notifications of a user.\n        ---\n\n        parameters:\n            - name: Authorization\n              description: Bearer token to make an authenticated request.\n              required: true\n              paramType: header\n            - name: page\n              description: Restricts output to the given page number.\n              required: false\n              paramType: query\n            - name: page_size\n              description: Sets the number of notifications per page.\n              required: false\n              paramType: query\n            - name: search\n              description: Filters by title.\n              required: false\n              paramType: query\n            - name: ordering\n              description: Sorts the results. You can order by (-)pubdate or (-)title.\n              paramType: query\n            - name: type\n              description: Filters by notification type.\n              paramType: query\n            - name: subscription_type\n              description: Filters by subscription type.\n              paramType: query\n            - name: expand\n              description: Returns an object instead of an identifier representing the given field.\n              required: false\n              paramType: query\n        responseMessages:\n            - code: 401\n              message: Not Authenticated\n            - code: 404\n              message: Not Found\n        \"\"\"\n        return self.list(request, *args, **kwargs)\n\n    def get_queryset(self):\n        queryset = Notification.objects.get_notifications_of(self.request.user)\n        subscription_type = self.request.query_params.get('subscription_type', None)\n        if subscription_type:\n            queryset = queryset.filter(subscription__content_type__model=subscription_type)\n        _type = self.request.query_params.get('type', None)\n        if _type:\n            queryset = queryset.filter(content_type__model=_type)\n        return queryset\n", "path": "zds/notification/api/views.py"}]}