Add REST API to webinterface
This commit is contained in:
parent
b528d4dbb4
commit
0c8ba5eb87
|
@ -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):
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
46
api/views.py
46
api/views.py
|
@ -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']
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Django<1.12
|
Django<1.12
|
||||||
django-crispy-forms
|
django-crispy-forms
|
||||||
django-rest-framework
|
django-rest-framework
|
||||||
|
django-filter
|
||||||
|
|
Loading…
Reference in New Issue