Start REST API for autocompletion etc.
This commit is contained in:
parent
ec0a8c98e7
commit
3a68972c03
25
shimatta_kenkyusho/api/ExpiringAuthToken.py
Normal file
25
shimatta_kenkyusho/api/ExpiringAuthToken.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from rest_framework.authentication import TokenAuthentication
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework import exceptions
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
|
EXPIRE_HOURS = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_HOURS', 24)
|
||||||
|
|
||||||
|
class ExpiringTokenAuthentication(TokenAuthentication):
|
||||||
|
def authenticate_credentials(self, key):
|
||||||
|
print(key)
|
||||||
|
try:
|
||||||
|
token = Token.objects.get(key=key)
|
||||||
|
except Token.DoesNotExist:
|
||||||
|
raise exceptions.AuthenticationFailed('Invalid token')
|
||||||
|
|
||||||
|
if not token.user.is_active:
|
||||||
|
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
||||||
|
|
||||||
|
if token.created < timezone.now() - timedelta(hours=EXPIRE_HOURS):
|
||||||
|
raise exceptions.AuthenticationFailed('Token has expired')
|
||||||
|
|
||||||
|
return (token.user, token)
|
0
shimatta_kenkyusho/api/__init__.py
Normal file
0
shimatta_kenkyusho/api/__init__.py
Normal file
3
shimatta_kenkyusho/api/admin.py
Normal file
3
shimatta_kenkyusho/api/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
shimatta_kenkyusho/api/apps.py
Normal file
6
shimatta_kenkyusho/api/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'api'
|
0
shimatta_kenkyusho/api/migrations/__init__.py
Normal file
0
shimatta_kenkyusho/api/migrations/__init__.py
Normal file
3
shimatta_kenkyusho/api/models.py
Normal file
3
shimatta_kenkyusho/api/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
52
shimatta_kenkyusho/api/serializers.py
Normal file
52
shimatta_kenkyusho/api/serializers.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from django.contrib.auth.models import User, Group
|
||||||
|
from rest_framework import serializers
|
||||||
|
from parts import models as parts_models
|
||||||
|
|
||||||
|
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['username', 'email', 'first_name', 'last_name', 'groups']
|
||||||
|
|
||||||
|
class GroupSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Group
|
||||||
|
fields = ['url', 'id', 'url', 'name']
|
||||||
|
|
||||||
|
class PackageSerializerNoLink(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Package
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class PackageSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Package
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class StorageSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
full_path = serializers.ReadOnlyField(source='get_full_path')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Storage
|
||||||
|
fields = ['url', 'id', 'name', 'parent_storage', 'responsible', 'full_path']
|
||||||
|
|
||||||
|
class ComponentSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
|
||||||
|
package_data = PackageSerializerNoLink(source='package', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Component
|
||||||
|
fields = ['url', 'id', 'name', 'package_data', 'package', 'pref_distri']
|
||||||
|
|
||||||
|
class StockSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
ro_package_name = serializers.ReadOnlyField(source='component.package.name')
|
||||||
|
ro_component_name = serializers.ReadOnlyField(source='component.name')
|
||||||
|
ro_manufacturer_name = serializers.ReadOnlyField(source='component.manufacturer.name')
|
||||||
|
ro_image = serializers.ReadOnlyField(source='component.get_resolved_image')
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Stock
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class DistributorSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = parts_models.Distributor
|
||||||
|
fields = '__all__'
|
3
shimatta_kenkyusho/api/tests.py
Normal file
3
shimatta_kenkyusho/api/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
19
shimatta_kenkyusho/api/urls.py
Normal file
19
shimatta_kenkyusho/api/urls.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from django.urls import include, path
|
||||||
|
from rest_framework import routers
|
||||||
|
from .views import *
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'users', UserViewSet)
|
||||||
|
router.register(r'groups', GroupViewSet)
|
||||||
|
router.register(r'parts/storages', PartsStorageViewSet)
|
||||||
|
router.register(r'parts/components', PartsComponentViewSet)
|
||||||
|
router.register(r'parts/stocks', PartsStockViewSet)
|
||||||
|
router.register(r'parts/packages', PartsPackageViewSet)
|
||||||
|
router.register(r'parts/distributors', PartsDistributorviewSet)
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', include(router.urls)),
|
||||||
|
url(r'^token-auth/', ObtainExpiringAuthToken.as_view()),
|
||||||
|
url(r'^token-logout/', TokenLogout.as_view()),
|
||||||
|
]
|
98
shimatta_kenkyusho/api/views.py
Normal file
98
shimatta_kenkyusho/api/views.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
from django.contrib.auth.models import User, Group
|
||||||
|
from rest_framework import viewsets, status
|
||||||
|
from rest_framework import permissions
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from .serializers import *
|
||||||
|
from parts import models as parts_models
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from django.db.models.deletion import ProtectedError
|
||||||
|
from django.db.models import F
|
||||||
|
import datetime
|
||||||
|
from datetime import timedelta
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from rest_framework.authtoken.views import ObtainAuthToken
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.throttling import AnonRateThrottle
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint that allows users to be viewed or edited.
|
||||||
|
"""
|
||||||
|
queryset = User.objects.all()
|
||||||
|
serializer_class = UserSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
class GroupViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint that allows users to be viewed or edited.
|
||||||
|
"""
|
||||||
|
queryset = Group.objects.all()
|
||||||
|
serializer_class = GroupSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
class PartsStorageViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = parts_models.Storage.objects.all()
|
||||||
|
serializer_class = StorageSerializer
|
||||||
|
permission_classes = [permissions.DjangoModelPermissions]
|
||||||
|
|
||||||
|
class PartsComponentViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = parts_models.Component.objects.all()
|
||||||
|
serializer_class = ComponentSerializer
|
||||||
|
permission_classes = [permissions.DjangoModelPermissions]
|
||||||
|
|
||||||
|
class PartsStockViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = parts_models.Stock.objects.all()
|
||||||
|
serializer_class = StockSerializer
|
||||||
|
permission_classes = [permissions.DjangoModelPermissions]
|
||||||
|
|
||||||
|
class PartsPackageViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = parts_models.Package.objects.all()
|
||||||
|
serializer_class = PackageSerializer
|
||||||
|
permission_classes = [permissions.DjangoModelPermissions]
|
||||||
|
|
||||||
|
class PartsDistributorviewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = parts_models.Distributor.objects.all()
|
||||||
|
serializer_class = DistributorSerializer
|
||||||
|
permission_classes = [permissions.DjangoModelPermissions]
|
||||||
|
|
||||||
|
|
||||||
|
## Token Authentication views
|
||||||
|
|
||||||
|
EXPIRE_HOURS = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_HOURS', 24)
|
||||||
|
|
||||||
|
|
||||||
|
class ObtainExpiringAuthToken(ObtainAuthToken):
|
||||||
|
throttle_classes = [AnonRateThrottle]
|
||||||
|
def post(self, request):
|
||||||
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
try:
|
||||||
|
token = Token.objects.get(user=serializer.validated_data['user'])
|
||||||
|
if token.created < timezone.now() - timedelta(hours=EXPIRE_HOURS):
|
||||||
|
token.delete()
|
||||||
|
except Token.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
token, created = Token.objects.get_or_create(user=serializer.validated_data['user'])
|
||||||
|
|
||||||
|
if not created:
|
||||||
|
# update the created time of the token to keep it valid
|
||||||
|
token.created = datetime.datetime.utcnow()
|
||||||
|
token.save()
|
||||||
|
|
||||||
|
return Response({'token': token.key, 'username': serializer.validated_data['user'].username, 'expiry': token.created + timedelta(hours=EXPIRE_HOURS)})
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenLogout(APIView):
|
||||||
|
def post(self, request, format=None):
|
||||||
|
try:
|
||||||
|
request.user.auth_token.delete()
|
||||||
|
except AttributeError as e:
|
||||||
|
pass
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
def get(self, request, format=None):
|
||||||
|
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
@ -40,6 +40,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'parts.apps.PartsConfig',
|
'parts.apps.PartsConfig',
|
||||||
'qr_code',
|
'qr_code',
|
||||||
|
'rest_framework'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@ -113,6 +114,27 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework.authentication.BasicAuthentication',
|
||||||
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
|
'api.ExpiringAuthToken.ExpiringTokenAuthentication',
|
||||||
|
],
|
||||||
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||||
|
'PAGE_SIZE': 10,
|
||||||
|
'DEFAULT_THROTTLE_CLASSES': [
|
||||||
|
'rest_framework.throttling.AnonRateThrottle',
|
||||||
|
'rest_framework.throttling.UserRateThrottle'
|
||||||
|
],
|
||||||
|
'DEFAULT_THROTTLE_RATES': {
|
||||||
|
'anon': '100/hour',
|
||||||
|
'user': '2000/hour'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
REST_FRAMEWORK_TOKEN_EXPIRE_HOURS = 4
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||||
|
@ -23,5 +23,6 @@ from parts import views as parts_views
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^admin/login/', parts_views.login_view),
|
url(r'^admin/login/', parts_views.login_view),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('api/v1/', include('api.urls')),
|
||||||
path('', include('parts.urls')),
|
path('', include('parts.urls')),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
Loading…
Reference in New Issue
Block a user