From f09d4cd5cff92a2c1892518705ec4ddf7501eb94 Mon Sep 17 00:00:00 2001 From: Domagoj Zecevic Date: Tue, 6 Jan 2026 13:37:11 +0100 Subject: [PATCH] added restapi --- core/permissions.py | 16 +++++++ divebases/api.py | 9 ++++ divebases/serializers.py | 7 +++ divemanager/urls.py | 16 ++++++- events/api.py | 27 +++++++++++ ...ription_event_google_maps_link_and_more.py | 48 +++++++++++++++++++ events/models.py | 32 ++++++++----- events/serializers.py | 23 +++++++++ infos/api.py | 15 ++++++ infos/serializers.py | 10 ++++ users/api.py | 17 +++++++ users/serializers.py | 19 ++++++++ 12 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 core/permissions.py create mode 100644 divebases/api.py create mode 100644 divebases/serializers.py create mode 100644 events/api.py create mode 100644 events/migrations/0003_event_description_event_google_maps_link_and_more.py create mode 100644 events/serializers.py create mode 100644 infos/api.py create mode 100644 infos/serializers.py create mode 100644 users/api.py create mode 100644 users/serializers.py diff --git a/core/permissions.py b/core/permissions.py new file mode 100644 index 0000000..a55db90 --- /dev/null +++ b/core/permissions.py @@ -0,0 +1,16 @@ +from rest_framework.permissions import BasePermission + +class IsDiveBaseAdmin(BasePermission): + """ + Allows access only to admins or superusers. + """ + def has_permission(self, request, view): + return bool(request.user and request.user.is_authenticated and request.user.role == "ADMIN" or request.user.is_superuser) + + +class IsSameDiveBase(BasePermission): + """ + Allows access only to objects in the same dive base as the user. + """ + def has_object_permission(self, request, view, obj): + return obj.dive_base == request.user.dive_base diff --git a/divebases/api.py b/divebases/api.py new file mode 100644 index 0000000..946a36d --- /dev/null +++ b/divebases/api.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets +from .models import DiveBase +from .serializers import DiveBaseSerializer +from rest_framework.permissions import IsAuthenticated + +class DiveBaseViewSet(viewsets.ModelViewSet): + serializer_class = DiveBaseSerializer + permission_classes = [IsAuthenticated] + queryset = DiveBase.objects.all() diff --git a/divebases/serializers.py b/divebases/serializers.py new file mode 100644 index 0000000..0cd076b --- /dev/null +++ b/divebases/serializers.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from .models import DiveBase + +class DiveBaseSerializer(serializers.ModelSerializer): + class Meta: + model = DiveBase + fields = ['id', 'name', 'location'] diff --git a/divemanager/urls.py b/divemanager/urls.py index a3c4a89..9162be7 100644 --- a/divemanager/urls.py +++ b/divemanager/urls.py @@ -1,7 +1,19 @@ - from django.contrib import admin -from django.urls import path +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from users.api import UserViewSet +from divebases.api import DiveBaseViewSet +from events.api import EventViewSet, EventApplicationViewSet +from infos.api import InfoArticleViewSet + +router = DefaultRouter() +router.register(r'users', UserViewSet, basename='user') +router.register(r'divebases', DiveBaseViewSet, basename='divebase') +router.register(r'events', EventViewSet, basename='event') +router.register(r'event-applications', EventApplicationViewSet, basename='eventapplication') +router.register(r'infos', InfoArticleViewSet, basename='info') urlpatterns = [ path('admin/', admin.site.urls), + path('api/', include(router.urls)), ] diff --git a/events/api.py b/events/api.py new file mode 100644 index 0000000..24f6bba --- /dev/null +++ b/events/api.py @@ -0,0 +1,27 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated +from .models import Event, EventApplication +from .serializers import EventSerializer, EventApplicationSerializer +from core.permissions import IsSameDiveBase, IsDiveBaseAdmin + +class EventViewSet(viewsets.ModelViewSet): + serializer_class = EventSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return Event.objects.filter(dive_base=self.request.user.dive_base, is_deleted=False) + + def perform_destroy(self, instance): + instance.is_deleted = True + instance.save() + + +class EventApplicationViewSet(viewsets.ModelViewSet): + serializer_class = EventApplicationSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return EventApplication.objects.filter(user=self.request.user) + + def perform_create(self, serializer): + serializer.save(user=self.request.user) diff --git a/events/migrations/0003_event_description_event_google_maps_link_and_more.py b/events/migrations/0003_event_description_event_google_maps_link_and_more.py new file mode 100644 index 0000000..372eab7 --- /dev/null +++ b/events/migrations/0003_event_description_event_google_maps_link_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.9 on 2026-01-06 12:26 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='description', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='event', + name='google_maps_link', + field=models.URLField(blank=True), + ), + migrations.AddField( + model_name='event', + name='location', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddField( + model_name='event', + name='payment_details', + field=models.TextField(blank=True), + ), + migrations.CreateModel( + name='EventApplication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('paid', models.BooleanField(default=False)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='events.event')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_applications', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('event', 'user')}, + }, + ), + ] diff --git a/events/models.py b/events/models.py index 8a3d722..6243c8e 100644 --- a/events/models.py +++ b/events/models.py @@ -1,26 +1,32 @@ from django.db import models from django.conf import settings +from divebases.models import DiveBase User = settings.AUTH_USER_MODEL - class Event(models.Model): title = models.CharField(max_length=255) start_datetime = models.DateTimeField() + location = models.CharField(max_length=255, blank=True) + google_maps_link = models.URLField(blank=True) + description = models.TextField(blank=True) + payment_details = models.TextField(blank=True) - dive_base = models.ForeignKey( - 'divebases.DiveBase', - on_delete=models.CASCADE, - related_name='events' - ) - - created_by = models.ForeignKey( - User, - on_delete=models.PROTECT, - related_name='created_events' - ) - + dive_base = models.ForeignKey(DiveBase, on_delete=models.CASCADE, related_name='events') + created_by = models.ForeignKey(User, on_delete=models.PROTECT, related_name='created_events') is_deleted = models.BooleanField(default=False) def __str__(self): return self.title + + +class EventApplication(models.Model): + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='applications') + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='event_applications') + paid = models.BooleanField(default=False) + + class Meta: + unique_together = ('event', 'user') # prevent duplicate applications + + def __str__(self): + return f"{self.user} applied for {self.event}" diff --git a/events/serializers.py b/events/serializers.py new file mode 100644 index 0000000..a79948a --- /dev/null +++ b/events/serializers.py @@ -0,0 +1,23 @@ +from rest_framework import serializers +from .models import Event, EventApplication +from divebases.serializers import DiveBaseSerializer +from users.serializers import UserSerializer + +class EventSerializer(serializers.ModelSerializer): + dive_base = DiveBaseSerializer(read_only=True) + + class Meta: + model = Event + fields = ['id', 'title', 'start_datetime', 'location', 'google_maps_link', + 'description', 'payment_details', 'dive_base', 'is_deleted'] + +class EventApplicationSerializer(serializers.ModelSerializer): + user = UserSerializer(read_only=True) + event_id = serializers.PrimaryKeyRelatedField( + source='event', queryset=Event.objects.all(), write_only=True + ) + + class Meta: + model = EventApplication + fields = ['id', 'event', 'event_id', 'user', 'paid'] + read_only_fields = ['id', 'user'] diff --git a/infos/api.py b/infos/api.py new file mode 100644 index 0000000..48d1550 --- /dev/null +++ b/infos/api.py @@ -0,0 +1,15 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated +from .models import InfoArticle +from .serializers import InfoArticleSerializer + +class InfoArticleViewSet(viewsets.ModelViewSet): + serializer_class = InfoArticleSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return InfoArticle.objects.filter(dive_base=self.request.user.dive_base, is_deleted=False) + + def perform_destroy(self, instance): + instance.is_deleted = True + instance.save() diff --git a/infos/serializers.py b/infos/serializers.py new file mode 100644 index 0000000..dc7c574 --- /dev/null +++ b/infos/serializers.py @@ -0,0 +1,10 @@ +from rest_framework import serializers +from .models import InfoArticle +from divebases.serializers import DiveBaseSerializer + +class InfoArticleSerializer(serializers.ModelSerializer): + dive_base = DiveBaseSerializer(read_only=True) + + class Meta: + model = InfoArticle + fields = ['id', 'title', 'short_description', 'content', 'dive_base', 'is_deleted'] diff --git a/users/api.py b/users/api.py new file mode 100644 index 0000000..ce3811f --- /dev/null +++ b/users/api.py @@ -0,0 +1,17 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated +from .models import User +from .serializers import UserSerializer +from core.permissions import IsDiveBaseAdmin + +class UserViewSet(viewsets.ModelViewSet): + serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + user = self.request.user + if user.is_superuser: + return User.objects.all() + if user.role == "ADMIN": + return User.objects.filter(dive_base=user.dive_base) + return User.objects.filter(id=user.id) diff --git a/users/serializers.py b/users/serializers.py new file mode 100644 index 0000000..156aa9c --- /dev/null +++ b/users/serializers.py @@ -0,0 +1,19 @@ +from rest_framework import serializers +from .models import User +from divebases.models import DiveBase + +class DiveBaseSerializer(serializers.ModelSerializer): + class Meta: + model = DiveBase + fields = ['id', 'name', 'location'] + +class UserSerializer(serializers.ModelSerializer): + dive_base = DiveBaseSerializer(read_only=True) + dive_base_id = serializers.PrimaryKeyRelatedField( + source='dive_base', queryset=DiveBase.objects.all(), write_only=True, required=False + ) + + class Meta: + model = User + fields = ['id', 'username', 'email', 'role', 'dive_certificate', 'dive_base', 'dive_base_id'] + read_only_fields = ['id']