Add REST API to webinterface

This commit is contained in:
Sebastian Lohff 2020-02-02 02:10:33 +01:00
parent ddb09d148e
commit e825685105
7 changed files with 92 additions and 14 deletions

View File

@ -1,6 +1,9 @@
from django import forms
from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers
from contest.models import Contest, Band, Frequency, QSO, EntryCategory, User, ShadowCall, Reference from contest.models import Contest, Band, Frequency, QSO, EntryCategory, User, ShadowCall, Reference
from contest.validators import CallLogValidator
class ContestSerializer(serializers.ModelSerializer): class ContestSerializer(serializers.ModelSerializer):
@ -39,20 +42,52 @@ class ReferenceSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
ref = ReferenceSerializer() # ref = ReferenceSerializer()
cat = EntryCategorySerializer() # cat = EntryCategorySerializer()
class Meta: class Meta:
model = User model = User
fields = ('id', 'ref', 'cat', 'location', 'opName', 'regTime', 'dncall', 'qrv2m', 'qrv70cm', 'extra2m70cm') fields = ('id', 'ref', 'cat', 'location', 'opName', 'regTime', 'dncall', 'qrv2m', 'qrv70cm', 'extra2m70cm')
read_only_fields = ('ref', 'location', 'regTime')
def validate(self, attrs):
contest = Contest.get_current_contest()
if contest.deadline < timezone.now():
raise serializers.ValidationError("The deadline for changing the entry category has passed")
return attrs
class QSOSerializer(serializers.ModelSerializer): class QSOSerializer(serializers.ModelSerializer):
# owner = UserSerializer()
class Meta: class Meta:
model = QSO model = QSO
fields = ('id', 'owner', 'time', 'call', 'callRef', 'remarks') fields = ('id', 'owner', 'time', 'ownNo', 'band', 'call', 'reportTX', 'reportRX', 'refStr', 'remarks')
read_only_fields = ('owner',)
def validate_call(self, value):
val = value.strip().upper()
try:
CallLogValidator()(val)
except forms.ValidationError as e:
raise serializers.ValidationError({'errors': e.error_list})
return val
def validate(self, attrs):
ownNo = attrs['ownNo']
try:
o = QSO.objects.get(owner=self.context['request'].user, ownNo=ownNo)
if not (self.instance and self.instance.id and self.instance.id == o.id):
raise serializers.ValidationError("You already logged a QSO with the number %s" % ownNo)
except QSO.DoesNotExist:
pass
band = attrs.get('band')
if band:
if band.contest.deadline < timezone.now():
raise serializers.ValidationError("The deadline for logging and editing QSOs has passed")
return attrs
class ShadowCallSerializer(serializers.ModelSerializer): class ShadowCallSerializer(serializers.ModelSerializer):

View File

@ -2,7 +2,7 @@ from django.conf.urls import include, url
from rest_framework import routers from rest_framework import routers
from .views import ContestViewSet, BandViewSet, FrequencyViewSet, EntryCategoryViewSet, ReferenceViewSet, QSOViewSet, \ from .views import ContestViewSet, BandViewSet, FrequencyViewSet, EntryCategoryViewSet, ReferenceViewSet, QSOViewSet, \
ShadowCallViewSet ShadowCallViewSet, UserProfileViewSet
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('contests', ContestViewSet) router.register('contests', ContestViewSet)
@ -12,6 +12,7 @@ router.register('entrycategories', EntryCategoryViewSet)
router.register('references', ReferenceViewSet) router.register('references', ReferenceViewSet)
router.register('qsos', QSOViewSet, basename='qso') router.register('qsos', QSOViewSet, basename='qso')
router.register('shadowcalls', ShadowCallViewSet) router.register('shadowcalls', ShadowCallViewSet)
router.register('profile', UserProfileViewSet, basename='profile')
urlpatterns = [ urlpatterns = [
url(r'^', include(router.urls)), url(r'^', include(router.urls)),

View File

@ -1,52 +1,84 @@
from rest_framework import viewsets from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, generics, filters
from rest_framework.permissions import IsAuthenticated, IsAdminUser from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from .serializers import ContestSerializer, BandSerializer, FrequencySerializer, EntryCategorySerializer, \ from .serializers import ContestSerializer, BandSerializer, FrequencySerializer, EntryCategorySerializer, \
ReferenceSerializer, QSOSerializer, ShadowCallSerializer ReferenceSerializer, QSOSerializer, ShadowCallSerializer, UserSerializer
from contest.models import Contest, Band, Frequency, EntryCategory, Reference, QSO, ShadowCall from contest.models import Contest, Band, Frequency, EntryCategory, Reference, QSO, ShadowCall
class ContestViewSet(viewsets.ReadOnlyModelViewSet): class ContestViewSet(viewsets.ReadOnlyModelViewSet):
"""
Resource to list and view all available contests. Use `current/` to get the current Contest.
"""
queryset = Contest.objects.all() queryset = Contest.objects.all()
serializer_class = ContestSerializer serializer_class = ContestSerializer
filterset_fields = ['shortName']
def get_object(self):
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
if self.kwargs.get(lookup_url_kwarg) == "current":
obj = Contest.get_current_contest()
self.check_object_permissions(self.request, obj)
else:
obj = super(ContestViewSet, self).get_object()
return obj
class BandViewSet(viewsets.ReadOnlyModelViewSet): class BandViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Band.objects.all() queryset = Band.objects.all()
serializer_class = BandSerializer serializer_class = BandSerializer
filterset_fields = ['name', 'contest']
class FrequencyViewSet(viewsets.ReadOnlyModelViewSet): class FrequencyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Frequency.objects.all() queryset = Frequency.objects.all()
serializer_class = FrequencySerializer serializer_class = FrequencySerializer
filterset_fields = ['band', 'channel']
class EntryCategoryViewSet(viewsets.ReadOnlyModelViewSet): class EntryCategoryViewSet(viewsets.ReadOnlyModelViewSet):
queryset = EntryCategory.objects.all() queryset = EntryCategory.objects.all()
serializer_class = EntryCategorySerializer serializer_class = EntryCategorySerializer
filterset_fields = ['name']
class ReferenceViewSet(viewsets.ReadOnlyModelViewSet): class ReferenceViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAdminUser] permission_classes = [IsAdminUser]
queryset = Reference.objects.all() queryset = Reference.objects.all()
serializer_class = ReferenceSerializer serializer_class = ReferenceSerializer
filterset_fields = ['name']
class QSOViewSet(viewsets.ReadOnlyModelViewSet): class QSOViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
serializer_class = QSOSerializer serializer_class = QSOSerializer
filterset_fields = ['time', 'ownNo', 'band', 'call', 'refStr']
def get_queryset(self): def get_queryset(self):
return QSO.objects.filter(owner=self.request.user) return QSO.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
return serializer.save(owner=self.request.user)
class UserProfile(viewsets.ReadOnlyModelViewSet):
"""Return the currently authenticated user as a single item""" class UserProfileViewSet(generics.UpdateAPIView, viewsets.GenericViewSet):
pass permission_classes = [IsAuthenticated]
serializer_class = UserSerializer
def list(self, request, format=None):
user = request.user
serializer = UserSerializer(user)
return Response(serializer.data)
def get_queryset(self):
return self.request.user
class ShadowCallViewSet(viewsets.ReadOnlyModelViewSet): class ShadowCallViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAdminUser] permission_classes = [IsAdminUser]
queryset = ShadowCall.objects.all() queryset = ShadowCall.objects.all()
serializer_class = ShadowCallSerializer serializer_class = ShadowCallSerializer
filterset_fields = ['username', 'ref']

View File

@ -22,6 +22,11 @@ class Contest(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_current_contest(cls):
return cls.objects.get(id=1)
class Reference(models.Model): class Reference(models.Model):
name = models.CharField(max_length=20, unique=True, db_index=True) name = models.CharField(max_length=20, unique=True, db_index=True)
description = models.TextField() description = models.TextField()

View File

@ -25,4 +25,3 @@ class CallLogValidator(validators.RegexValidator):
'e.g. DL7BST, DN1BER-1, DL/OE1FOO, DN1FTW-1/p' 'e.g. DL7BST, DN1BER-1, DL/OE1FOO, DN1FTW-1/p'
) )
flags = re.ASCII if six.PY3 else 0 flags = re.ASCII if six.PY3 else 0

View File

@ -42,6 +42,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'crispy_forms', 'crispy_forms',
'rest_framework', 'rest_framework',
'django_filters',
# local # local
'contest', 'contest',
@ -143,3 +144,7 @@ CRISPY_TEMPLATE_PACK = 'bootstrap3'
MESSAGE_TAGS = { MESSAGE_TAGS = {
messages.ERROR: 'danger', messages.ERROR: 'danger',
} }
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

View File

@ -1,3 +1,4 @@
Django<1.12 Django<1.12
django-crispy-forms django-crispy-forms
django-rest-framework django-rest-framework
django-filter