We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support deleting infractions
The bot tries to delete infractions that fail to apply on the Discord side of things. This is cause it sends the infraction to our API before applying it on Discord. However, our API doesn't actually support the delete method so it returns a 405.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pydis_site/apps/api/viewsets/bot/infraction.py`
Content:
```
1 from django.http.request import HttpRequest
2 from django_filters.rest_framework import DjangoFilterBackend
3 from rest_framework.decorators import action
4 from rest_framework.exceptions import ValidationError
5 from rest_framework.filters import OrderingFilter, SearchFilter
6 from rest_framework.mixins import (
7     CreateModelMixin,
8     ListModelMixin,
9     RetrieveModelMixin
10 )
11 from rest_framework.response import Response
12 from rest_framework.viewsets import GenericViewSet
13 
14 from pydis_site.apps.api.models.bot.infraction import Infraction
15 from pydis_site.apps.api.serializers import (
16     ExpandedInfractionSerializer,
17     InfractionSerializer
18 )
19 
20 
21 class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):
22     """
23     View providing CRUD operations on infractions for Discord users.
24 
25     ## Routes
26     ### GET /bot/infractions
27     Retrieve all infractions.
28     May be filtered by the query parameters.
29 
30     #### Query parameters
31     - **active** `bool`: whether the infraction is still active
32     - **actor__id** `int`: snowflake of the user which applied the infraction
33     - **hidden** `bool`: whether the infraction is a shadow infraction
34     - **search** `str`: regular expression applied to the infraction's reason
35     - **type** `str`: the type of the infraction
36     - **user__id** `int`: snowflake of the user to which the infraction was applied
37     - **ordering** `str`: comma-separated sequence of fields to order the returned results
38 
39     Invalid query parameters are ignored.
40 
41     #### Response format
42     >>> [
43     ...     {
44     ...         'id': 5,
45     ...         'inserted_at': '2018-11-22T07:24:06.132307Z',
46     ...         'expires_at': '5018-11-20T15:52:00Z',
47     ...         'active': False,
48     ...         'user': 172395097705414656,
49     ...         'actor': 125435062127820800,
50     ...         'type': 'ban',
51     ...         'reason': 'He terk my jerb!',
52     ...         'hidden': True
53     ...     }
54     ... ]
55 
56     #### Status codes
57     - 200: returned on success
58 
59     ### GET /bot/infractions/<id:int>
60     Retrieve a single infraction by ID.
61 
62     #### Response format
63     See `GET /bot/infractions`.
64 
65     #### Status codes
66     - 200: returned on success
67     - 404: if an infraction with the given `id` could not be found
68 
69     ### POST /bot/infractions
70     Create a new infraction and return the created infraction.
71     Only `actor`, `type`, and `user` are required.
72     The `actor` and `user` must be users known by the site.
73 
74     #### Request body
75     >>> {
76     ...     'active': False,
77     ...     'actor': 125435062127820800,
78     ...     'expires_at': '5018-11-20T15:52:00+00:00',
79     ...     'hidden': True,
80     ...     'type': 'ban',
81     ...     'reason': 'He terk my jerb!',
82     ...     'user': 172395097705414656
83     ... }
84 
85     #### Response format
86     See `GET /bot/infractions`.
87 
88     #### Status codes
89     - 201: returned on success
90     - 400: if a given user is unknown or a field in the request body is invalid
91 
92     ### PATCH /bot/infractions/<id:int>
93     Update the infraction with the given `id` and return the updated infraction.
94     Only `active`, `reason`, and `expires_at` may be updated.
95 
96     #### Request body
97     >>> {
98     ...     'active': True,
99     ...     'expires_at': '4143-02-15T21:04:31+00:00',
100     ...     'reason': 'durka derr'
101     ... }
102 
103     #### Response format
104     See `GET /bot/infractions`.
105 
106     #### Status codes
107     - 200: returned on success
108     - 400: if a field in the request body is invalid or disallowed
109     - 404: if an infraction with the given `id` could not be found
110 
111     ### Expanded routes
112     All routes support expansion of `user` and `actor` in responses. To use an expanded route,
113     append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.
114 
115     #### Response format
116     See `GET /bot/users/<snowflake:int>` for the expanded formats of `user` and `actor`. Responses
117     are otherwise identical to their non-expanded counterparts.
118     """
119 
120     serializer_class = InfractionSerializer
121     queryset = Infraction.objects.all()
122     filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
123     filter_fields = ('user__id', 'actor__id', 'active', 'hidden', 'type')
124     search_fields = ('$reason',)
125     frozen_fields = ('id', 'inserted_at', 'type', 'user', 'actor', 'hidden')
126 
127     def partial_update(self, request: HttpRequest, *_args, **_kwargs) -> Response:
128         """Method that handles the nuts and bolts of updating an Infraction."""
129         for field in request.data:
130             if field in self.frozen_fields:
131                 raise ValidationError({field: ['This field cannot be updated.']})
132 
133         instance = self.get_object()
134         serializer = self.get_serializer(instance, data=request.data, partial=True)
135         serializer.is_valid(raise_exception=True)
136         serializer.save()
137 
138         return Response(serializer.data)
139 
140     @action(url_path='expanded', detail=False)
141     def list_expanded(self, *args, **kwargs) -> Response:
142         """
143         DRF method for listing Infraction entries.
144 
145         Called by the Django Rest Framework in response to the corresponding HTTP request.
146         """
147         self.serializer_class = ExpandedInfractionSerializer
148         return self.list(*args, **kwargs)
149 
150     @list_expanded.mapping.post
151     def create_expanded(self, *args, **kwargs) -> Response:
152         """
153         DRF method for creating an Infraction.
154 
155         Called by the Django Rest Framework in response to the corresponding HTTP request.
156         """
157         self.serializer_class = ExpandedInfractionSerializer
158         return self.create(*args, **kwargs)
159 
160     @action(url_path='expanded', url_name='detail-expanded', detail=True)
161     def retrieve_expanded(self, *args, **kwargs) -> Response:
162         """
163         DRF method for retrieving a specific Infraction.
164 
165         Called by the Django Rest Framework in response to the corresponding HTTP request.
166         """
167         self.serializer_class = ExpandedInfractionSerializer
168         return self.retrieve(*args, **kwargs)
169 
170     @retrieve_expanded.mapping.patch
171     def partial_update_expanded(self, *args, **kwargs) -> Response:
172         """
173         DRF method for updating an Infraction.
174 
175         Called by the Django Rest Framework in response to the corresponding HTTP request.
176         """
177         self.serializer_class = ExpandedInfractionSerializer
178         return self.partial_update(*args, **kwargs)
179
```
--- 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/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py
--- a/pydis_site/apps/api/viewsets/bot/infraction.py
+++ b/pydis_site/apps/api/viewsets/bot/infraction.py
@@ -5,6 +5,7 @@
 from rest_framework.filters import OrderingFilter, SearchFilter
 from rest_framework.mixins import (
     CreateModelMixin,
+    DestroyModelMixin,
     ListModelMixin,
     RetrieveModelMixin
 )
@@ -18,7 +19,13 @@
 )
 
 
-class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):
+class InfractionViewSet(
+    CreateModelMixin,
+    RetrieveModelMixin,
+    ListModelMixin,
+    GenericViewSet,
+    DestroyModelMixin
+):
     """
     View providing CRUD operations on infractions for Discord users.
 
@@ -108,6 +115,13 @@
     - 400: if a field in the request body is invalid or disallowed
     - 404: if an infraction with the given `id` could not be found
 
+    ### DELETE /bot/infractions/<id:int>
+    Delete the infraction with the given `id`.
+
+    #### Status codes
+    - 204: returned on success
+    - 404: if a infraction with the given `id` does not exist
+
     ### Expanded routes
     All routes support expansion of `user` and `actor` in responses. To use an expanded route,
     append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.

{"golden_diff": "diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py\n--- a/pydis_site/apps/api/viewsets/bot/infraction.py\n+++ b/pydis_site/apps/api/viewsets/bot/infraction.py\n@@ -5,6 +5,7 @@\n from rest_framework.filters import OrderingFilter, SearchFilter\n from rest_framework.mixins import (\n     CreateModelMixin,\n+    DestroyModelMixin,\n     ListModelMixin,\n     RetrieveModelMixin\n )\n@@ -18,7 +19,13 @@\n )\n \n \n-class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):\n+class InfractionViewSet(\n+    CreateModelMixin,\n+    RetrieveModelMixin,\n+    ListModelMixin,\n+    GenericViewSet,\n+    DestroyModelMixin\n+):\n     \"\"\"\n     View providing CRUD operations on infractions for Discord users.\n \n@@ -108,6 +115,13 @@\n     - 400: if a field in the request body is invalid or disallowed\n     - 404: if an infraction with the given `id` could not be found\n \n+    ### DELETE /bot/infractions/<id:int>\n+    Delete the infraction with the given `id`.\n+\n+    #### Status codes\n+    - 204: returned on success\n+    - 404: if a infraction with the given `id` does not exist\n+\n     ### Expanded routes\n     All routes support expansion of `user` and `actor` in responses. To use an expanded route,\n     append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.\n", "issue": "Support deleting infractions\nThe bot tries to delete infractions that fail to apply on the Discord side of things. This is cause it sends the infraction to our API before applying it on Discord. However, our API doesn't actually support the delete method so it returns a 405.\n", "before_files": [{"content": "from django.http.request import HttpRequest\nfrom django_filters.rest_framework import DjangoFilterBackend\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.filters import OrderingFilter, SearchFilter\nfrom rest_framework.mixins import (\n    CreateModelMixin,\n    ListModelMixin,\n    RetrieveModelMixin\n)\nfrom rest_framework.response import Response\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom pydis_site.apps.api.models.bot.infraction import Infraction\nfrom pydis_site.apps.api.serializers import (\n    ExpandedInfractionSerializer,\n    InfractionSerializer\n)\n\n\nclass InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):\n    \"\"\"\n    View providing CRUD operations on infractions for Discord users.\n\n    ## Routes\n    ### GET /bot/infractions\n    Retrieve all infractions.\n    May be filtered by the query parameters.\n\n    #### Query parameters\n    - **active** `bool`: whether the infraction is still active\n    - **actor__id** `int`: snowflake of the user which applied the infraction\n    - **hidden** `bool`: whether the infraction is a shadow infraction\n    - **search** `str`: regular expression applied to the infraction's reason\n    - **type** `str`: the type of the infraction\n    - **user__id** `int`: snowflake of the user to which the infraction was applied\n    - **ordering** `str`: comma-separated sequence of fields to order the returned results\n\n    Invalid query parameters are ignored.\n\n    #### Response format\n    >>> [\n    ...     {\n    ...         'id': 5,\n    ...         'inserted_at': '2018-11-22T07:24:06.132307Z',\n    ...         'expires_at': '5018-11-20T15:52:00Z',\n    ...         'active': False,\n    ...         'user': 172395097705414656,\n    ...         'actor': 125435062127820800,\n    ...         'type': 'ban',\n    ...         'reason': 'He terk my jerb!',\n    ...         'hidden': True\n    ...     }\n    ... ]\n\n    #### Status codes\n    - 200: returned on success\n\n    ### GET /bot/infractions/<id:int>\n    Retrieve a single infraction by ID.\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 200: returned on success\n    - 404: if an infraction with the given `id` could not be found\n\n    ### POST /bot/infractions\n    Create a new infraction and return the created infraction.\n    Only `actor`, `type`, and `user` are required.\n    The `actor` and `user` must be users known by the site.\n\n    #### Request body\n    >>> {\n    ...     'active': False,\n    ...     'actor': 125435062127820800,\n    ...     'expires_at': '5018-11-20T15:52:00+00:00',\n    ...     'hidden': True,\n    ...     'type': 'ban',\n    ...     'reason': 'He terk my jerb!',\n    ...     'user': 172395097705414656\n    ... }\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 201: returned on success\n    - 400: if a given user is unknown or a field in the request body is invalid\n\n    ### PATCH /bot/infractions/<id:int>\n    Update the infraction with the given `id` and return the updated infraction.\n    Only `active`, `reason`, and `expires_at` may be updated.\n\n    #### Request body\n    >>> {\n    ...     'active': True,\n    ...     'expires_at': '4143-02-15T21:04:31+00:00',\n    ...     'reason': 'durka derr'\n    ... }\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 200: returned on success\n    - 400: if a field in the request body is invalid or disallowed\n    - 404: if an infraction with the given `id` could not be found\n\n    ### Expanded routes\n    All routes support expansion of `user` and `actor` in responses. To use an expanded route,\n    append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.\n\n    #### Response format\n    See `GET /bot/users/<snowflake:int>` for the expanded formats of `user` and `actor`. Responses\n    are otherwise identical to their non-expanded counterparts.\n    \"\"\"\n\n    serializer_class = InfractionSerializer\n    queryset = Infraction.objects.all()\n    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)\n    filter_fields = ('user__id', 'actor__id', 'active', 'hidden', 'type')\n    search_fields = ('$reason',)\n    frozen_fields = ('id', 'inserted_at', 'type', 'user', 'actor', 'hidden')\n\n    def partial_update(self, request: HttpRequest, *_args, **_kwargs) -> Response:\n        \"\"\"Method that handles the nuts and bolts of updating an Infraction.\"\"\"\n        for field in request.data:\n            if field in self.frozen_fields:\n                raise ValidationError({field: ['This field cannot be updated.']})\n\n        instance = self.get_object()\n        serializer = self.get_serializer(instance, data=request.data, partial=True)\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n\n        return Response(serializer.data)\n\n    @action(url_path='expanded', detail=False)\n    def list_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for listing Infraction entries.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.list(*args, **kwargs)\n\n    @list_expanded.mapping.post\n    def create_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for creating an Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.create(*args, **kwargs)\n\n    @action(url_path='expanded', url_name='detail-expanded', detail=True)\n    def retrieve_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for retrieving a specific Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.retrieve(*args, **kwargs)\n\n    @retrieve_expanded.mapping.patch\n    def partial_update_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for updating an Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.partial_update(*args, **kwargs)\n", "path": "pydis_site/apps/api/viewsets/bot/infraction.py"}], "after_files": [{"content": "from django.http.request import HttpRequest\nfrom django_filters.rest_framework import DjangoFilterBackend\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.filters import OrderingFilter, SearchFilter\nfrom rest_framework.mixins import (\n    CreateModelMixin,\n    DestroyModelMixin,\n    ListModelMixin,\n    RetrieveModelMixin\n)\nfrom rest_framework.response import Response\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom pydis_site.apps.api.models.bot.infraction import Infraction\nfrom pydis_site.apps.api.serializers import (\n    ExpandedInfractionSerializer,\n    InfractionSerializer\n)\n\n\nclass InfractionViewSet(\n    CreateModelMixin,\n    RetrieveModelMixin,\n    ListModelMixin,\n    GenericViewSet,\n    DestroyModelMixin\n):\n    \"\"\"\n    View providing CRUD operations on infractions for Discord users.\n\n    ## Routes\n    ### GET /bot/infractions\n    Retrieve all infractions.\n    May be filtered by the query parameters.\n\n    #### Query parameters\n    - **active** `bool`: whether the infraction is still active\n    - **actor__id** `int`: snowflake of the user which applied the infraction\n    - **hidden** `bool`: whether the infraction is a shadow infraction\n    - **search** `str`: regular expression applied to the infraction's reason\n    - **type** `str`: the type of the infraction\n    - **user__id** `int`: snowflake of the user to which the infraction was applied\n    - **ordering** `str`: comma-separated sequence of fields to order the returned results\n\n    Invalid query parameters are ignored.\n\n    #### Response format\n    >>> [\n    ...     {\n    ...         'id': 5,\n    ...         'inserted_at': '2018-11-22T07:24:06.132307Z',\n    ...         'expires_at': '5018-11-20T15:52:00Z',\n    ...         'active': False,\n    ...         'user': 172395097705414656,\n    ...         'actor': 125435062127820800,\n    ...         'type': 'ban',\n    ...         'reason': 'He terk my jerb!',\n    ...         'hidden': True\n    ...     }\n    ... ]\n\n    #### Status codes\n    - 200: returned on success\n\n    ### GET /bot/infractions/<id:int>\n    Retrieve a single infraction by ID.\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 200: returned on success\n    - 404: if an infraction with the given `id` could not be found\n\n    ### POST /bot/infractions\n    Create a new infraction and return the created infraction.\n    Only `actor`, `type`, and `user` are required.\n    The `actor` and `user` must be users known by the site.\n\n    #### Request body\n    >>> {\n    ...     'active': False,\n    ...     'actor': 125435062127820800,\n    ...     'expires_at': '5018-11-20T15:52:00+00:00',\n    ...     'hidden': True,\n    ...     'type': 'ban',\n    ...     'reason': 'He terk my jerb!',\n    ...     'user': 172395097705414656\n    ... }\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 201: returned on success\n    - 400: if a given user is unknown or a field in the request body is invalid\n\n    ### PATCH /bot/infractions/<id:int>\n    Update the infraction with the given `id` and return the updated infraction.\n    Only `active`, `reason`, and `expires_at` may be updated.\n\n    #### Request body\n    >>> {\n    ...     'active': True,\n    ...     'expires_at': '4143-02-15T21:04:31+00:00',\n    ...     'reason': 'durka derr'\n    ... }\n\n    #### Response format\n    See `GET /bot/infractions`.\n\n    #### Status codes\n    - 200: returned on success\n    - 400: if a field in the request body is invalid or disallowed\n    - 404: if an infraction with the given `id` could not be found\n\n    ### DELETE /bot/infractions/<id:int>\n    Delete the infraction with the given `id`.\n\n    #### Status codes\n    - 204: returned on success\n    - 404: if a infraction with the given `id` does not exist\n\n    ### Expanded routes\n    All routes support expansion of `user` and `actor` in responses. To use an expanded route,\n    append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.\n\n    #### Response format\n    See `GET /bot/users/<snowflake:int>` for the expanded formats of `user` and `actor`. Responses\n    are otherwise identical to their non-expanded counterparts.\n    \"\"\"\n\n    serializer_class = InfractionSerializer\n    queryset = Infraction.objects.all()\n    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)\n    filter_fields = ('user__id', 'actor__id', 'active', 'hidden', 'type')\n    search_fields = ('$reason',)\n    frozen_fields = ('id', 'inserted_at', 'type', 'user', 'actor', 'hidden')\n\n    def partial_update(self, request: HttpRequest, *_args, **_kwargs) -> Response:\n        \"\"\"Method that handles the nuts and bolts of updating an Infraction.\"\"\"\n        for field in request.data:\n            if field in self.frozen_fields:\n                raise ValidationError({field: ['This field cannot be updated.']})\n\n        instance = self.get_object()\n        serializer = self.get_serializer(instance, data=request.data, partial=True)\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n\n        return Response(serializer.data)\n\n    @action(url_path='expanded', detail=False)\n    def list_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for listing Infraction entries.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.list(*args, **kwargs)\n\n    @list_expanded.mapping.post\n    def create_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for creating an Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.create(*args, **kwargs)\n\n    @action(url_path='expanded', url_name='detail-expanded', detail=True)\n    def retrieve_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for retrieving a specific Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.retrieve(*args, **kwargs)\n\n    @retrieve_expanded.mapping.patch\n    def partial_update_expanded(self, *args, **kwargs) -> Response:\n        \"\"\"\n        DRF method for updating an Infraction.\n\n        Called by the Django Rest Framework in response to the corresponding HTTP request.\n        \"\"\"\n        self.serializer_class = ExpandedInfractionSerializer\n        return self.partial_update(*args, **kwargs)\n", "path": "pydis_site/apps/api/viewsets/bot/infraction.py"}]}