Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,8 @@

# Custom User Model
AUTH_USER_MODEL = "hoagiecalendar.User"

# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
4 changes: 3 additions & 1 deletion backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@

from hoagiecalendar.api.event_views import EventDetailView, EventView
from hoagiecalendar.api.user_events_view import UserEventsView
from hoagiecalendar.api.user_saved_events_view import UserSavedEventsView

urlpatterns = [
path("admin/", admin.site.urls),
path("event/", EventView.as_view(), name="event"),
path("event/<int:event_id>/", EventDetailView.as_view(), name="event-detail"),
path("user/events/", UserEventsView.as_view(), name="user-events"),
path("user/events/created", UserEventsView.as_view(), name="user-events"),
path("user/events/saved", UserSavedEventsView.as_view(), name="user-events-saved"),
]
10 changes: 9 additions & 1 deletion backend/hoagiecalendar/api/event_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class EventSerializer(serializers.ModelSerializer):
},
)

is_saved = serializers.SerializerMethodField()

def get_is_saved(self, obj: Event) -> bool:
user = self.context["request"].user

return obj.saved_by.contains(user)

Comment on lines +40 to +46

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger a query per event when serializing to get whether the user saved the event. Can we do a query set annotation instead where we take the events we want to serialize, make a query to add the is_saved field into the query set, and then put the annotated query set into the serializer? This way we only make one query for all the events we want to serialize. Make sure all of the calls to the Event Serializer are updated to reflect changes (merge from preview first).

class Meta:
model = Event
fields = [
Expand All @@ -51,8 +58,9 @@ class Meta:
"category",
"from_mail",
"ordering",
"is_saved_by_user",
]
read_only_fields = ["id", "owner", "created_at", "updated_at"]
read_only_fields = ["id", "owner", "created_at", "updated_at", "is_saved_by_user"]


class EventView(APIView):
Expand Down
2 changes: 1 addition & 1 deletion backend/hoagiecalendar/api/user_events_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ class UserEventsView(APIView):
# Logic to get events user created
def get(self, request) -> Response:
events = Event.objects.filter(owner=request.user)
serializer = EventSerializer(events, many=True)
serializer = EventSerializer(events, many=True, context={"request": request})
return Response(serializer.data, status=status.HTTP_200_OK)
60 changes: 60 additions & 0 deletions backend/hoagiecalendar/api/user_saved_events_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import TypedDict, cast

from rest_framework import serializers, status
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

from hoagiecalendar.api.user_events_view import EventSerializer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import from event_views

from hoagiecalendar.models import Event


class SaveEventRequestSerializer(serializers.Serializer):
event_id = serializers.BigIntegerField()


class SaveEventRequestData(TypedDict):
event_id: int


class UserSavedEventsView(APIView):
def get(self, request: Request) -> Response:
user = request.user
saved_events = user.saved_events.all()
serializer = EventSerializer(saved_events, many=True, context={"request": request})

return Response(serializer.data, status=status.HTTP_200_OK)

def post(self, request: Request) -> Response:
serializer = SaveEventRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializer.errors doesn't need to be nested in the dict. just put it in directly


validated_data = cast(SaveEventRequestData, serializer.validated_data)

try:
event = Event.objects.get(pk=validated_data["event_id"])
event.saved_by.add(request.user)

return Response({"message": "event saved"}, status=status.HTTP_201_CREATED)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's return the updated event serialized here with status 200 since no new object was created.

except Event.DoesNotExist:
return Response({"error": "event not found"}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({"error": f"an error occured while saving event for user: {e}"})

@jfmath04 jfmath04 Apr 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs a status 500.

also, can we leave out the actual error e and just return that there was an internal error? Write a TODO comment to log the error detail once we add logging.


def delete(self, request: Request) -> Response:
serializer = SaveEventRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializer.errors doesn't need to be nested in the dict. just put it in directly


validated_data = cast(SaveEventRequestData, serializer.validated_data)

try:
event = Event.objects.get(pk=validated_data["event_id"])
event.saved_by.add(request.user)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be delete


return Response({"message": "event unsaved"}, status=status.HTTP_200_OK)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be status 204. Also no message needed since there is no data to return.

except Event.DoesNotExist:
return Response({"error": "event not found"}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({"error": f"an error occured while unsaving event for user: {e}"})

@jfmath04 jfmath04 Apr 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also needs a status 500.

also, can we leave out the actual error e and just return that there was an internal error? Write a TODO comment to log the error detail once we add logging.

1 change: 1 addition & 0 deletions backend/hoagiecalendar/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Event(models.Model):
from_mail = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
saved_by = models.ManyToManyField(User, related_name="saved_events", blank=True)

def __str__(self) -> str:
return self.name
Expand Down
Loading