Сериализация содержимого курса
Необходимо сериализовать содержимое курса. Модель Content содержит универсальный внешний ключ, который позволяет связывать объекты различных моделей содержимого. Тем не менее, в предыдущей главе мы добавили общий метод render() для всех моделей содержимого. Этот метод можно использовать для предоставления содержимого нашему API.
Отредактируйте файл api/serializers.py приложения courses и добавьте в него следующий код:
from ..models import Content
class ItemRelatedField(serializers.RelatedField):
def to_representation(self, value):
return value.render()
class ContentSerializer(serializers.ModelSerializer):
item = ItemRelatedField(read_only=True)
class Meta:
model = Content
fields = ('order', 'item')
В этом коде мы определяем настраиваемое поле, подклассом сериализатора RelatedField, предоставляемым REST Framework, и переопределением метода to_representation(). Мы определяем сериализатор ContentSerializer для модели Content и используем настраиваемое поле item для генерации foreign key.
Нам нужен альтернативный сериализатор для модели Module, включающий его содержимое, а также расширенный сериализатор Course. Измените файл api/serializers.py и добавьте в него следующий код:
class ModuleWithContentsSerializer(serializers.ModelSerializer):
contents = ContentSerializer(many=True)
class Meta:
model = Module
fields = ('order', 'title', 'description', 'contents')
class CourseWithContentsSerializer(serializers.ModelSerializer):
modules = ModuleWithContentsSerializer(many=True)
class Meta:
model = Course
fields = ('id', 'subject', 'title', 'slug', 'overview', 'created', 'owner', 'modules')
Давайте создадим представление, которое имитирует поведение действия retrieve(), но включает содержимое курса. Измените файл api/views.py и добавьте следующий метод в класс CourseViewSet:
from .permissions import IsEnrolled
from .serializers import CourseWithContentsSerializer
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
# ...
@detail_route(methods=['get'],
serializer_class=CourseWithContentsSerializer,
authentication_classes=[BasicAuthentication],
permission_classes=[IsAuthenticated, IsEnrolled])
def contents(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
Описание этого метода выглядит следующим образом:
- Мы используем декоратор detail_route, чтобы указать, что это действие выполняется для одного объекта.
- Мы указываем, что для этого действия разрешен только метод GET.
- Мы используем новый класс сериализатора CourseWithContentsSerializer, который включает содержимое курса.
- Мы используем IsAuthenticated, так и кастомный IsEnrolled. Таким образом, мы убедимся, что только пользователи, зарегистрировавшиеся на курс, смогут получить доступ к его содержимому.
- Для возврата объекта курса используется существующее действие retrieve().
Откройте в браузере http://127.0.0.1:8000/api/courses/1/contents/ . При доступе к просмотру с помощью правильных учетных данных будет видно, что каждый модуль курса включает в себя отображаемый HTML-код для содержимого курса, например:
{
"order": 0,
"title": "Installing Django",
"description": "",
"contents": [
{
"order": 0,
"item": "<p>Take a look at the following video for installing Django:</p>\n"
},
{
"order": 1,
"item": "\n<iframe width=\"480\" height=\"360\" src=\"http://www.youtube.com/embed/bgV39DlmZ2U?wmode=opaque\" frameborder=\"0\"allowfullscreen></iframe>\n\n"
}
]
}
Вы создали простой интерфейс API, позволяющий другим службам программным образом обращаться к приложению курса. REST Framework также позволяет управлять созданием и редактированием объектов с помощью набора представлений ModelViewSet. Мы покрыли основные аспекты Django Rest Framework, но вы найдете дополнительную информацию о ее функциях в ее обширной документации здесь: http://www.django-rest-framework.org/