We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Liked photos API returns duplicate photos on different api requests
### Describe the bug
Different pages of the liked photos API return the same photo

### How to reproduce
Difficult, this is using @JeeVee11's account as I could not reproduce it. 

https://staging.thalia.nu/api/v2/photos/photos/liked/?limit=10&offset=0
https://staging.thalia.nu/api/v2/photos/photos/liked/?limit=30&offset=10

Both have pk=70
### Expected behaviour
this should kinda not happen

### Additional context
This is likely related to photos not following order and https://github.com/svthalia/Reaxit/issues/359


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `website/photos/api/v2/views.py`
Content:
```
1 from django.db.models import Count, Prefetch, Q
2 
3 from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
4 from rest_framework import filters, status
5 from rest_framework.exceptions import PermissionDenied
6 from rest_framework.generics import ListAPIView, RetrieveAPIView
7 from rest_framework.response import Response
8 from rest_framework.views import APIView
9 
10 from photos import services
11 from photos.api.v2.serializers.album import (
12     AlbumListSerializer,
13     AlbumSerializer,
14     PhotoListSerializer,
15 )
16 from photos.models import Album, Like, Photo
17 from utils.media.services import fetch_thumbnails_db
18 
19 
20 class AlbumListView(ListAPIView):
21     """Returns an overview of all albums."""
22 
23     serializer_class = AlbumListSerializer
24 
25     def get_serializer(self, *args, **kwargs):
26         if len(args) > 0:
27             albums = args[0]
28             fetch_thumbnails_db([album.cover.file for album in albums if album.cover])
29         return super().get_serializer(*args, **kwargs)
30 
31     queryset = Album.objects.filter(hidden=False).select_related("_cover")
32 
33     permission_classes = [
34         IsAuthenticatedOrTokenHasScope,
35     ]
36     required_scopes = ["photos:read"]
37     filter_backends = (filters.SearchFilter,)
38     search_fields = ("title", "date", "slug")
39 
40 
41 class AlbumDetailView(RetrieveAPIView):
42     """Returns the details of an album."""
43 
44     serializer_class = AlbumSerializer
45     permission_classes = [
46         IsAuthenticatedOrTokenHasScope,
47     ]
48     required_scopes = ["photos:read"]
49     lookup_field = "slug"
50 
51     def retrieve(self, request, *args, **kwargs):
52         if not services.is_album_accessible(request, self.get_object()):
53             raise PermissionDenied
54         return super().retrieve(request, *args, **kwargs)
55 
56     def get_object(self):
57         object = super().get_object()
58         fetch_thumbnails_db([photo.file for photo in object.photo_set.all()])
59         return object
60 
61     def get_queryset(self):
62         photos = Photo.objects.select_properties("num_likes")
63         if self.request.member:
64             photos = photos.annotate(
65                 member_likes=Count("likes", filter=Q(likes__member=self.request.member))
66             )
67 
68         # Fix select_properties dropping the default ordering.
69         photos = photos.order_by("pk")
70 
71         return Album.objects.filter(hidden=False).prefetch_related(
72             Prefetch("photo_set", queryset=photos)
73         )
74 
75 
76 class LikedPhotosListView(ListAPIView):
77     """Returns the details the liked album."""
78 
79     serializer_class = PhotoListSerializer
80     permission_classes = [
81         IsAuthenticatedOrTokenHasScope,
82     ]
83     required_scopes = ["photos:read"]
84 
85     def get(self, request, *args, **kwargs):
86         if not self.request.member:
87             return Response(
88                 data={
89                     "detail": "You need to be a member in order to view your liked photos."
90                 },
91                 status=status.HTTP_403_FORBIDDEN,
92             )
93         return self.list(request, *args, **kwargs)
94 
95     def get_serializer(self, *args, **kwargs):
96         if len(args) > 0:
97             photos = args[0]
98             fetch_thumbnails_db([photo.file for photo in photos])
99         return super().get_serializer(*args, **kwargs)
100 
101     def get_queryset(self):
102         return (
103             Photo.objects.filter(likes__member=self.request.member, album__hidden=False)
104             .annotate(
105                 member_likes=Count("likes", filter=Q(likes__member=self.request.member))
106             )
107             .select_properties("num_likes")
108         )
109 
110 
111 class PhotoLikeView(APIView):
112     permission_classes = [IsAuthenticatedOrTokenHasScope]
113     required_scopes = ["photos:read"]
114 
115     def get(self, request, **kwargs):
116         photo_id = kwargs.get("pk")
117         try:
118             photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)
119         except Photo.DoesNotExist:
120             return Response(status=status.HTTP_404_NOT_FOUND)
121 
122         return Response(
123             {
124                 "liked": photo.likes.filter(member=request.member).exists(),
125                 "num_likes": photo.num_likes,
126             },
127             status=status.HTTP_200_OK,
128         )
129 
130     def post(self, request, **kwargs):
131         photo_id = kwargs.get("pk")
132         try:
133             photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)
134         except Photo.DoesNotExist:
135             return Response(status=status.HTTP_404_NOT_FOUND)
136 
137         _, created = Like.objects.get_or_create(photo=photo, member=request.member)
138 
139         if created:
140             return Response(
141                 {
142                     "liked": photo.likes.filter(member=request.member).exists(),
143                     "num_likes": photo.num_likes,
144                 },
145                 status=status.HTTP_201_CREATED,
146             )
147         return Response(
148             {
149                 "liked": photo.likes.filter(member=request.member).exists(),
150                 "num_likes": photo.num_likes,
151             },
152             status=status.HTTP_200_OK,
153         )
154 
155     def delete(self, request, **kwargs):
156         photo_id = kwargs.get("pk")
157         try:
158             photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)
159         except Photo.DoesNotExist:
160             return Response(status=status.HTTP_404_NOT_FOUND)
161 
162         try:
163             like = Like.objects.filter(photo__album__hidden=False).get(
164                 member=request.member, photo__pk=photo_id
165             )
166         except Like.DoesNotExist:
167             return Response(
168                 {
169                     "liked": False,
170                     "num_likes": photo.num_likes,
171                 },
172                 status=status.HTTP_204_NO_CONTENT,
173             )
174 
175         like.delete()
176 
177         return Response(
178             {
179                 "liked": False,
180                 "num_likes": photo.num_likes,
181             },
182             status=status.HTTP_202_ACCEPTED,
183         )
184
```
--- 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/website/photos/api/v2/views.py b/website/photos/api/v2/views.py
--- a/website/photos/api/v2/views.py
+++ b/website/photos/api/v2/views.py
@@ -105,6 +105,8 @@
                 member_likes=Count("likes", filter=Q(likes__member=self.request.member))
             )
             .select_properties("num_likes")
+            # Fix select_properties dropping the default ordering.
+            .order_by("pk")
         )

{"golden_diff": "diff --git a/website/photos/api/v2/views.py b/website/photos/api/v2/views.py\n--- a/website/photos/api/v2/views.py\n+++ b/website/photos/api/v2/views.py\n@@ -105,6 +105,8 @@\n                 member_likes=Count(\"likes\", filter=Q(likes__member=self.request.member))\n             )\n             .select_properties(\"num_likes\")\n+            # Fix select_properties dropping the default ordering.\n+            .order_by(\"pk\")\n         )\n", "issue": "Liked photos API returns duplicate photos on different api requests\n### Describe the bug\r\nDifferent pages of the liked photos API return the same photo\r\n\r\n### How to reproduce\r\nDifficult, this is using @JeeVee11's account as I could not reproduce it. \r\n\r\nhttps://staging.thalia.nu/api/v2/photos/photos/liked/?limit=10&offset=0\r\nhttps://staging.thalia.nu/api/v2/photos/photos/liked/?limit=30&offset=10\r\n\r\nBoth have pk=70\r\n### Expected behaviour\r\nthis should kinda not happen\r\n\r\n### Additional context\r\nThis is likely related to photos not following order and https://github.com/svthalia/Reaxit/issues/359\r\n\n", "before_files": [{"content": "from django.db.models import Count, Prefetch, Q\n\nfrom oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope\nfrom rest_framework import filters, status\nfrom rest_framework.exceptions import PermissionDenied\nfrom rest_framework.generics import ListAPIView, RetrieveAPIView\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nfrom photos import services\nfrom photos.api.v2.serializers.album import (\n    AlbumListSerializer,\n    AlbumSerializer,\n    PhotoListSerializer,\n)\nfrom photos.models import Album, Like, Photo\nfrom utils.media.services import fetch_thumbnails_db\n\n\nclass AlbumListView(ListAPIView):\n    \"\"\"Returns an overview of all albums.\"\"\"\n\n    serializer_class = AlbumListSerializer\n\n    def get_serializer(self, *args, **kwargs):\n        if len(args) > 0:\n            albums = args[0]\n            fetch_thumbnails_db([album.cover.file for album in albums if album.cover])\n        return super().get_serializer(*args, **kwargs)\n\n    queryset = Album.objects.filter(hidden=False).select_related(\"_cover\")\n\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n    filter_backends = (filters.SearchFilter,)\n    search_fields = (\"title\", \"date\", \"slug\")\n\n\nclass AlbumDetailView(RetrieveAPIView):\n    \"\"\"Returns the details of an album.\"\"\"\n\n    serializer_class = AlbumSerializer\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n    lookup_field = \"slug\"\n\n    def retrieve(self, request, *args, **kwargs):\n        if not services.is_album_accessible(request, self.get_object()):\n            raise PermissionDenied\n        return super().retrieve(request, *args, **kwargs)\n\n    def get_object(self):\n        object = super().get_object()\n        fetch_thumbnails_db([photo.file for photo in object.photo_set.all()])\n        return object\n\n    def get_queryset(self):\n        photos = Photo.objects.select_properties(\"num_likes\")\n        if self.request.member:\n            photos = photos.annotate(\n                member_likes=Count(\"likes\", filter=Q(likes__member=self.request.member))\n            )\n\n        # Fix select_properties dropping the default ordering.\n        photos = photos.order_by(\"pk\")\n\n        return Album.objects.filter(hidden=False).prefetch_related(\n            Prefetch(\"photo_set\", queryset=photos)\n        )\n\n\nclass LikedPhotosListView(ListAPIView):\n    \"\"\"Returns the details the liked album.\"\"\"\n\n    serializer_class = PhotoListSerializer\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n\n    def get(self, request, *args, **kwargs):\n        if not self.request.member:\n            return Response(\n                data={\n                    \"detail\": \"You need to be a member in order to view your liked photos.\"\n                },\n                status=status.HTTP_403_FORBIDDEN,\n            )\n        return self.list(request, *args, **kwargs)\n\n    def get_serializer(self, *args, **kwargs):\n        if len(args) > 0:\n            photos = args[0]\n            fetch_thumbnails_db([photo.file for photo in photos])\n        return super().get_serializer(*args, **kwargs)\n\n    def get_queryset(self):\n        return (\n            Photo.objects.filter(likes__member=self.request.member, album__hidden=False)\n            .annotate(\n                member_likes=Count(\"likes\", filter=Q(likes__member=self.request.member))\n            )\n            .select_properties(\"num_likes\")\n        )\n\n\nclass PhotoLikeView(APIView):\n    permission_classes = [IsAuthenticatedOrTokenHasScope]\n    required_scopes = [\"photos:read\"]\n\n    def get(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        return Response(\n            {\n                \"liked\": photo.likes.filter(member=request.member).exists(),\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_200_OK,\n        )\n\n    def post(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        _, created = Like.objects.get_or_create(photo=photo, member=request.member)\n\n        if created:\n            return Response(\n                {\n                    \"liked\": photo.likes.filter(member=request.member).exists(),\n                    \"num_likes\": photo.num_likes,\n                },\n                status=status.HTTP_201_CREATED,\n            )\n        return Response(\n            {\n                \"liked\": photo.likes.filter(member=request.member).exists(),\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_200_OK,\n        )\n\n    def delete(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        try:\n            like = Like.objects.filter(photo__album__hidden=False).get(\n                member=request.member, photo__pk=photo_id\n            )\n        except Like.DoesNotExist:\n            return Response(\n                {\n                    \"liked\": False,\n                    \"num_likes\": photo.num_likes,\n                },\n                status=status.HTTP_204_NO_CONTENT,\n            )\n\n        like.delete()\n\n        return Response(\n            {\n                \"liked\": False,\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_202_ACCEPTED,\n        )\n", "path": "website/photos/api/v2/views.py"}], "after_files": [{"content": "from django.db.models import Count, Prefetch, Q\n\nfrom oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope\nfrom rest_framework import filters, status\nfrom rest_framework.exceptions import PermissionDenied\nfrom rest_framework.generics import ListAPIView, RetrieveAPIView\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nfrom photos import services\nfrom photos.api.v2.serializers.album import (\n    AlbumListSerializer,\n    AlbumSerializer,\n    PhotoListSerializer,\n)\nfrom photos.models import Album, Like, Photo\nfrom utils.media.services import fetch_thumbnails_db\n\n\nclass AlbumListView(ListAPIView):\n    \"\"\"Returns an overview of all albums.\"\"\"\n\n    serializer_class = AlbumListSerializer\n\n    def get_serializer(self, *args, **kwargs):\n        if len(args) > 0:\n            albums = args[0]\n            fetch_thumbnails_db([album.cover.file for album in albums if album.cover])\n        return super().get_serializer(*args, **kwargs)\n\n    queryset = Album.objects.filter(hidden=False).select_related(\"_cover\")\n\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n    filter_backends = (filters.SearchFilter,)\n    search_fields = (\"title\", \"date\", \"slug\")\n\n\nclass AlbumDetailView(RetrieveAPIView):\n    \"\"\"Returns the details of an album.\"\"\"\n\n    serializer_class = AlbumSerializer\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n    lookup_field = \"slug\"\n\n    def retrieve(self, request, *args, **kwargs):\n        if not services.is_album_accessible(request, self.get_object()):\n            raise PermissionDenied\n        return super().retrieve(request, *args, **kwargs)\n\n    def get_object(self):\n        object = super().get_object()\n        fetch_thumbnails_db([photo.file for photo in object.photo_set.all()])\n        return object\n\n    def get_queryset(self):\n        photos = Photo.objects.select_properties(\"num_likes\")\n        if self.request.member:\n            photos = photos.annotate(\n                member_likes=Count(\"likes\", filter=Q(likes__member=self.request.member))\n            )\n\n        # Fix select_properties dropping the default ordering.\n        photos = photos.order_by(\"pk\")\n\n        return Album.objects.filter(hidden=False).prefetch_related(\n            Prefetch(\"photo_set\", queryset=photos)\n        )\n\n\nclass LikedPhotosListView(ListAPIView):\n    \"\"\"Returns the details the liked album.\"\"\"\n\n    serializer_class = PhotoListSerializer\n    permission_classes = [\n        IsAuthenticatedOrTokenHasScope,\n    ]\n    required_scopes = [\"photos:read\"]\n\n    def get(self, request, *args, **kwargs):\n        if not self.request.member:\n            return Response(\n                data={\n                    \"detail\": \"You need to be a member in order to view your liked photos.\"\n                },\n                status=status.HTTP_403_FORBIDDEN,\n            )\n        return self.list(request, *args, **kwargs)\n\n    def get_serializer(self, *args, **kwargs):\n        if len(args) > 0:\n            photos = args[0]\n            fetch_thumbnails_db([photo.file for photo in photos])\n        return super().get_serializer(*args, **kwargs)\n\n    def get_queryset(self):\n        return (\n            Photo.objects.filter(likes__member=self.request.member, album__hidden=False)\n            .annotate(\n                member_likes=Count(\"likes\", filter=Q(likes__member=self.request.member))\n            )\n            .select_properties(\"num_likes\")\n            # Fix select_properties dropping the default ordering.\n            .order_by(\"pk\")\n        )\n\n\nclass PhotoLikeView(APIView):\n    permission_classes = [IsAuthenticatedOrTokenHasScope]\n    required_scopes = [\"photos:read\"]\n\n    def get(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        return Response(\n            {\n                \"liked\": photo.likes.filter(member=request.member).exists(),\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_200_OK,\n        )\n\n    def post(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        _, created = Like.objects.get_or_create(photo=photo, member=request.member)\n\n        if created:\n            return Response(\n                {\n                    \"liked\": photo.likes.filter(member=request.member).exists(),\n                    \"num_likes\": photo.num_likes,\n                },\n                status=status.HTTP_201_CREATED,\n            )\n        return Response(\n            {\n                \"liked\": photo.likes.filter(member=request.member).exists(),\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_200_OK,\n        )\n\n    def delete(self, request, **kwargs):\n        photo_id = kwargs.get(\"pk\")\n        try:\n            photo = Photo.objects.filter(album__hidden=False).get(pk=photo_id)\n        except Photo.DoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n\n        try:\n            like = Like.objects.filter(photo__album__hidden=False).get(\n                member=request.member, photo__pk=photo_id\n            )\n        except Like.DoesNotExist:\n            return Response(\n                {\n                    \"liked\": False,\n                    \"num_likes\": photo.num_likes,\n                },\n                status=status.HTTP_204_NO_CONTENT,\n            )\n\n        like.delete()\n\n        return Response(\n            {\n                \"liked\": False,\n                \"num_likes\": photo.num_likes,\n            },\n            status=status.HTTP_202_ACCEPTED,\n        )\n", "path": "website/photos/api/v2/views.py"}]}