Merge remote-tracking branch 'origin/feature/api' into feature/api

remotes/origin/hasaccess
Vitaly Baev 8 years ago
commit 33b6a7e0cc
  1. 33
      api/v1/__init__.py
  2. 35
      api/v1/permissions.py
  3. 55
      api/v1/serializers.py
  4. 3
      api/v1/urls.py
  5. 177
      api/v1/views.py
  6. 17
      apps/course/migrations/0021_auto_20180206_0632.py
  7. 1
      apps/course/models.py
  8. 9
      apps/user/models.py
  9. 6
      project/settings.py

@ -1,4 +1,11 @@
from rest_framework import viewsets
import imghdr
import base64
import six
import uuid
from django.core.files.base import ContentFile
from rest_framework import serializers, viewsets
from rest_framework.response import Response
# https://gist.github.com/ivlevdenis/a0c8f5b472b6b8550bbb016c6a30e0be
@ -41,3 +48,27 @@ class ExtendViewSet(object):
class ExtendedModelViewSet(ExtendViewSet, viewsets.ModelViewSet):
pass
class Base64ImageField(serializers.ImageField):
def to_internal_value(self, data):
if isinstance(data, six.string_types):
if 'data:' in data and ';base64,' in data:
header, data = data.split(';base64,')
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(
file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension,)
data = ContentFile(decoded_file, name=complete_file_name)
return super().to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension

@ -0,0 +1,35 @@
from django.contrib.auth import get_user_model
from rest_framework.permissions import BasePermission
User = get_user_model()
class IsAdmin(BasePermission):
def has_permission(self, request, view):
return request.user.is_authenticated and (
request.user.role == User.ADMIN_ROLE or request.user.is_staff or request.user.is_superuser
)
class IsAdminOrIsSelf(BasePermission):
def has_object_permission(self, request, view, user):
return request.user.is_authenticated and (
user == request.user or request.user.is_staff or request.user.is_superuser
)
class IsAuthorOrAdmin(BasePermission):
def has_permission(self, request, view):
return request.user.is_authenticated and (
request.user.role in [
User.AUTHOR_ROLE, User.ADMIN_ROLE
] or request.user.is_staff or request.user.is_superuser
)
class IsAuthorObjectOrAdmin(BasePermission):
def has_object_permission(self, request, view, obj):
return request.user.is_authenticated and (
request.user.role == User.ADMIN_ROLE or request.user.is_staff or request.user.is_superuser
) and request.user == obj.author

@ -1,7 +1,9 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
from apps.course.models import Category, Course, Material, Like
from . import Base64ImageField
from apps.course.models import Category, Course, Material, Lesson, Like
from apps.content.models import (
Image, Text, ImageText, Video,
Gallery, GalleryImage,
@ -89,6 +91,29 @@ class CourseSerializer(serializers.ModelSerializer):
read_only_fields = (
'id',
'cover',
'created_at',
'update_at',
)
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = (
'id',
'title',
'short_description',
'course',
'cover',
'created_at',
'update_at',
)
read_only_fields = (
'id',
'cover',
'created_at',
'update_at',
)
@ -110,6 +135,7 @@ class ImageSerializer(serializers.ModelSerializer):
read_only_fields = (
'id',
'img',
'created_at',
'update_at',
)
@ -152,6 +178,7 @@ class ImageTextSerializer(serializers.ModelSerializer):
read_only_fields = (
'id',
'img',
'created_at',
'update_at',
)
@ -211,6 +238,7 @@ class GalleryImageSerializer(serializers.ModelSerializer):
read_only_fields = (
'id',
'image',
'created_at',
'update_at',
)
@ -248,8 +276,33 @@ class UserSerializer(serializers.ModelSerializer):
read_only_fields = (
'id',
'photo',
'date_joined',
'is_staff',
'fb_id',
'fb_data',
)
class CoverImageSerializer(serializers.Serializer):
cover = Base64ImageField(
required=False, allow_empty_file=True, allow_null=True
)
class UserPhotoSerializer(serializers.Serializer):
photo = Base64ImageField(
required=False, allow_empty_file=True, allow_null=True
)
class ContentImageSerializer(serializers.Serializer):
img = Base64ImageField(
required=False, allow_empty_file=True, allow_null=True
)
class GalleryImageSerializer(serializers.Serializer):
image = Base64ImageField(
required=False, allow_empty_file=True, allow_null=True
)

@ -12,13 +12,14 @@ from .views import (
ImageViewSet, TextViewSet,
ImageTextViewSet, VideoViewSet,
GalleryViewSet, GalleryImageViewSet,
UserViewSet,
UserViewSet, LessonViewSet,
)
router = DefaultRouter()
router.register(r'courses', CourseViewSet, base_name='courses')
router.register(r'categories', CategoryViewSet, base_name='categories')
router.register(r'materials', MaterialViewSet, base_name='materials')
router.register(r'lessons', LessonViewSet, base_name='lessons')
router.register(r'likes', LikeViewSet, base_name='likes')
router.register(r'images', ImageViewSet, base_name='images')

@ -1,6 +1,9 @@
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from . import ExtendedModelViewSet
from .serializers import (
@ -9,10 +12,13 @@ from .serializers import (
ImageSerializer, TextSerializer,
ImageTextSerializer, VideoSerializer,
GallerySerializer, GalleryImageSerializer,
UserSerializer,
UserSerializer, UserPhotoSerializer,
LessonSerializer, ContentImageSerializer,
GalleryImageSerializer, CoverImageSerializer,
)
from .permissions import IsAdmin, IsAdminOrIsSelf, IsAuthorOrAdmin, IsAuthorObjectOrAdmin
from apps.course.models import Category, Course, Material, Like
from apps.course.models import Category, Course, Material, Lesson, Like
from apps.content.models import (
Image, Text, ImageText, Video,
Gallery, GalleryImage,
@ -24,16 +30,25 @@ User = get_user_model()
class MaterialViewSet(ExtendedModelViewSet):
queryset = Material.objects.all()
serializer_class = MaterialSerializer
search_fields = ('title', 'short_description',)
ordering_fields = ('title', 'created_at', 'update_at',)
# permission_classes = (IsAdmin,)
class LikeViewSet(ExtendedModelViewSet):
queryset = Like.objects.select_related('user').all()
serializer_class = LikeSerializer
search_fields = ('user__email', 'user__firstname', 'user__lastname',)
ordering_fields = ('created_at', 'update_at',)
# permission_classes = (IsAdmin,)
class CategoryViewSet(ExtendedModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
search_fields = ('title',)
ordering_fields = ('title',)
# permission_classes = (IsAdmin,)
class CourseViewSet(ExtendedModelViewSet):
@ -43,7 +58,57 @@ class CourseViewSet(ExtendedModelViewSet):
'likes', 'materials'
).all()
serializer_class = CourseSerializer
filter_fields = ('category',)
serializer_class_map = {
'upload_photo': CoverImageSerializer,
}
filter_fields = ('category', 'status', 'is_infinite', 'is_featured',)
search_fields = ('author__email', 'title', 'category__title',)
ordering_fields = ('title', 'created_at', 'update_at',)
# permission_classes = (IsAuthorObjectOrAdmin,)
# permission_map = {
# 'create': IsAuthorOrAdmin,
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
course = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
course.cover = serialized_data['cover']
course.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)
class LessonViewSet(ExtendedModelViewSet):
queryset = Lesson.objects.select_related('course').all()
serializer_class = LessonSerializer
serializer_class_map = {
'upload_photo': CoverImageSerializer,
}
filter_fields = ('course',)
search_fields = ('title', 'short_description',)
ordering_fields = ('title', 'created_at', 'update_at',)
# permission_classes = (IsAuthorObjectOrAdmin,)
# permission_map = {
# 'create': IsAuthorOrAdmin,
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
lesson = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
lesson.cover = serialized_data['cover']
lesson.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)
class ImageViewSet(ExtendedModelViewSet):
@ -51,6 +116,27 @@ class ImageViewSet(ExtendedModelViewSet):
'course', 'lesson'
).all()
serializer_class = ImageSerializer
serializer_class_map = {
'upload_photo': ContentImageSerializer,
}
search_fields = ('title',)
ordering_fields = ('title', 'created_at', 'update_at', 'position',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
image = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
image.img = serialized_data['img']
image.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)
class TextViewSet(ExtendedModelViewSet):
@ -58,6 +144,12 @@ class TextViewSet(ExtendedModelViewSet):
'course', 'lesson'
).all()
serializer_class = TextSerializer
search_fields = ('title',)
ordering_fields = ('title', 'created_at', 'update_at', 'position',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
class ImageTextViewSet(ExtendedModelViewSet):
@ -65,6 +157,27 @@ class ImageTextViewSet(ExtendedModelViewSet):
'course', 'lesson'
).all()
serializer_class = ImageTextSerializer
serializer_class_map = {
'upload_photo': ContentImageSerializer,
}
search_fields = ('title',)
ordering_fields = ('title', 'created_at', 'update_at', 'position',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
image_text = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
image_text.img = serialized_data['img']
image_text.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)
class VideoViewSet(ExtendedModelViewSet):
@ -72,18 +185,76 @@ class VideoViewSet(ExtendedModelViewSet):
'course', 'lesson'
).all()
serializer_class = VideoSerializer
search_fields = ('title',)
ordering_fields = ('title', 'created_at', 'update_at', 'position',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
class GalleryViewSet(ExtendedModelViewSet):
queryset = Gallery.objects.select_related('course').all()
serializer_class = GallerySerializer
search_fields = ('title',)
ordering_fields = ('title', 'created_at', 'update_at',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
class GalleryImageViewSet(ExtendedModelViewSet):
queryset = GalleryImage.objects.select_related('gallery').all()
serializer_class = GalleryImageSerializer
serializer_class_map = {
'upload_photo': GalleryImageSerializer,
}
search_fields = ('gallery__title',)
# permission_classes = (IsAuthorOrAdmin,)
# permission_map = {
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
gallery_image = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
gallery_image.image = serialized_data['image']
gallery_image.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)
class UserViewSet(ExtendedModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
serializer_class_map = {
'upload_photo': UserPhotoSerializer,
}
filter_fields = ('is_staff', 'is_active', 'role',
'gender', 'is_email_proved', 'fb_id',)
search_fields = ('email', 'first_name', 'last_name',
'country', 'city', 'fb_id',)
ordering_fields = ('email', 'first_name', 'last_name',
'country', 'city', 'date_joined',)
# permission_classes = (IsAdminOrIsSelf,)
# permission_map = {
# 'delete': IsAdmin,
# }
@detail_route(methods=['post'], url_path='upload-photo')
def upload_photo(self, request, pk=None):
user = self.get_object()
serializer = self.get_serializer()
serialized_data = serializer(data=request.data)
if serialized_data.is_valid():
user.photo = serialized_data['photo']
user.save()
return Response({'success': True})
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST)

@ -0,0 +1,17 @@
# Generated by Django 2.0.2 on 2018-02-06 06:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('course', '0020_auto_20180202_1716'),
]
operations = [
migrations.AlterModelOptions(
name='category',
options={'ordering': ['title'], 'verbose_name': 'Категория', 'verbose_name_plural': 'Категории'},
),
]

@ -86,6 +86,7 @@ class Category(models.Model):
class Meta:
verbose_name = 'Категория'
verbose_name_plural = 'Категории'
ordering = ['title']
class Lesson(models.Model):

@ -5,10 +5,13 @@ from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
USER_ROLE = 0
AUTHOR_ROLE = 1
ADMIN_ROLE = 2
ROLE_CHOICES = (
(0, 'пользователь'),
(1, 'автор'),
(2, 'администратор'),
(USER_ROLE, 'пользователь'),
(AUTHOR_ROLE, 'автор'),
(ADMIN_ROLE, 'администратор'),
)
GENDER_CHOICES = (
('n', 'не указан'),

@ -206,3 +206,9 @@ REST_FRAMEWORK = {
if DEBUG:
CORS_ORIGIN_ALLOW_ALL = True
# Swagger doc settings
SWAGGER_SETTINGS = {
'DOC_EXPANSION': 'none',
}

Loading…
Cancel
Save