Refactor serialization code

This commit is contained in:
Daniel Egger 2022-10-14 10:32:53 +02:00
parent f8d00040f6
commit d8148158a1
10 changed files with 206 additions and 191 deletions

View File

@ -6,10 +6,16 @@ from wagtail.fields import StreamField
from wagtail.models import Page from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class from vbv_lernwelt.course.models import CourseBasePage
class CompetenceProfilePage(Page): class CompetenceProfilePage(CourseBasePage):
serialize_field_names = [
"course",
"circles",
"children",
]
parent_page_types = ["course.CoursePage"] parent_page_types = ["course.CoursePage"]
subpage_types = ["competence.CompetencePage"] subpage_types = ["competence.CompetencePage"]
@ -26,19 +32,13 @@ class CompetenceProfilePage(Page):
def get_frontend_url(self): def get_frontend_url(self):
return f"/competence/{self.slug}" return f"/competence/{self.slug}"
@classmethod
def get_serializer_class(cls): class CompetencePage(CourseBasePage):
return get_it_serializer_class( serialize_field_names = [
cls, "competence_id",
[
"course",
"circles",
"children", "children",
], ]
)
class CompetencePage(Page):
parent_page_types = ["competence.CompetenceProfilePage"] parent_page_types = ["competence.CompetenceProfilePage"]
subpage_types = ["competence.PerformanceCriteria"] subpage_types = ["competence.PerformanceCriteria"]
competence_id = models.TextField(default="A1") competence_id = models.TextField(default="A1")
@ -63,18 +63,8 @@ class CompetencePage(Page):
) )
super(CompetencePage, self).full_clean(*args, **kwargs) super(CompetencePage, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
[
"competence_id",
"children",
],
)
class PerformanceCriteria(CourseBasePage):
class PerformanceCriteria(Page):
parent_page_types = ["competence.CompetenceProfilePage"] parent_page_types = ["competence.CompetenceProfilePage"]
competence_id = models.TextField(default="A1.1") competence_id = models.TextField(default="A1.1")
learning_unit = models.ForeignKey( learning_unit = models.ForeignKey(

View File

@ -1,19 +1,16 @@
from rest_framework import serializers from rest_framework import serializers
from vbv_lernwelt.competence.models import PerformanceCriteria from vbv_lernwelt.competence.models import PerformanceCriteria
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class from vbv_lernwelt.course.serializers import (
from vbv_lernwelt.course.serializers import CourseCategorySerializer CourseCategorySerializer,
)
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
class PerformanceCriteriaSerializer( class PerformanceCriteriaSerializer(
get_it_serializer_class( get_course_serializer_class(
PerformanceCriteria, PerformanceCriteria,
[ field_names=[
"id",
"title",
"slug",
"type",
"translation_key",
"competence_id", "competence_id",
"learning_unit", "learning_unit",
"circle", "circle",
@ -43,14 +40,9 @@ class PerformanceCriteriaSerializer(
class PerformanceCriteriaLearningPathSerializer( class PerformanceCriteriaLearningPathSerializer(
get_it_serializer_class( get_course_serializer_class(
PerformanceCriteria, PerformanceCriteria,
[ field_names=[
"id",
"title",
"slug",
"type",
"translation_key",
"competence_id", "competence_id",
], ],
) )

View File

@ -1,12 +1,16 @@
import wagtail.api.v2.serializers as wagtail_serializers import wagtail.api.v2.serializers as wagtail_serializers
from rest_framework.fields import SerializerMethodField from rest_framework.fields import SerializerMethodField
from vbv_lernwelt.course.models import CoursePage
from vbv_lernwelt.course.serializers import CourseCategorySerializer, CourseSerializer
from vbv_lernwelt.learnpath.utils import get_wagtail_type from vbv_lernwelt.learnpath.utils import get_wagtail_type
def get_it_serializer_class(model, field_names): def get_it_serializer_class(
model, field_names=None, base_field_names=None, base_class=None
):
if field_names is None:
field_names = []
if base_field_names is None:
base_field_names = [ base_field_names = [
"id", "id",
"title", "title",
@ -15,27 +19,28 @@ def get_it_serializer_class(model, field_names):
"translation_key", "translation_key",
"frontend_url", "frontend_url",
] ]
if base_class is None:
base_class = ItWagtailBaseSerializer
return wagtail_serializers.get_serializer_class( return wagtail_serializers.get_serializer_class(
model, model,
field_names=base_field_names + field_names, field_names=base_field_names + field_names,
meta_fields=[], meta_fields=[],
base=ItBaseSerializer, base=base_class,
) )
class ItTypeField(wagtail_serializers.TypeField): class ItWagtailTypeField(wagtail_serializers.TypeField):
def to_representation(self, obj): def to_representation(self, obj):
name = get_wagtail_type(obj) name = get_wagtail_type(obj)
return name return name
class ItBaseSerializer(wagtail_serializers.BaseSerializer): class ItWagtailBaseSerializer(wagtail_serializers.BaseSerializer):
type = ItTypeField(read_only=True) type = ItWagtailTypeField(read_only=True)
children = SerializerMethodField() children = SerializerMethodField()
course = SerializerMethodField()
course_category = CourseCategorySerializer(read_only=True)
frontend_url = SerializerMethodField() frontend_url = SerializerMethodField()
circles = SerializerMethodField()
meta_fields = [] meta_fields = []
@ -54,36 +59,6 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
for c in children for c in children
] ]
def get_course(self, obj):
if hasattr(obj, "course"):
return CourseSerializer(obj.course).data
else:
course_parent_page = obj.get_ancestors().exact_type(CoursePage).last()
if course_parent_page:
return CourseSerializer(course_parent_page.specific.course).data
return ""
def get_circles(self, obj):
course_parent_page = obj.get_ancestors().exact_type(CoursePage).last()
if course_parent_page:
from vbv_lernwelt.learnpath.models import Circle, LearningPath
circles = (
course_parent_page.get_children()
.exact_type(LearningPath)
.first()
.get_children()
.exact_type(Circle)
)
return [
{"id": c.id, "title": c.title, "translation_key": c.translation_key}
for c in circles
]
return []
def get_frontend_url(self, obj): def get_frontend_url(self, obj):
if hasattr(obj, "get_frontend_url"): if hasattr(obj, "get_frontend_url"):
return obj.get_frontend_url() return obj.get_frontend_url()

View File

@ -6,6 +6,7 @@ from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.models import User from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
class Course(models.Model): class Course(models.Model):
@ -31,7 +32,60 @@ class CourseCategory(models.Model):
return f"{self.course} / {self.title}" return f"{self.course} / {self.title}"
class CoursePage(Page): class CourseBasePage(Page):
class Meta:
abstract = True
serialize_field_names = []
serialize_base_field_names = [
"id",
"title",
"slug",
"type",
"translation_key",
"frontend_url",
]
def get_course_parent(self):
return self.get_ancestors(inclusive=True).exact_type(CoursePage).last()
def get_course(self):
course_parent_page = self.get_course_parent()
if course_parent_page:
return course_parent_page.specific.course
return None
def get_circles(self):
course_parent_page = self.get_course_parent()
if course_parent_page:
from vbv_lernwelt.learnpath.models import LearningPath
from vbv_lernwelt.learnpath.models import Circle
circles = (
course_parent_page.get_children()
.exact_type(LearningPath)
.first()
.get_children()
.exact_type(Circle)
)
return circles
return None
@classmethod
def get_serializer_class(cls):
return get_course_serializer_class(
cls,
field_names=cls.serialize_field_names,
base_field_names=cls.serialize_base_field_names,
)
def __str__(self):
return f"{self.title}"
class CoursePage(CourseBasePage):
content_panels = Page.content_panels content_panels = Page.content_panels
subpage_types = ["learnpath.LearningPath", "media_library.MediaLibraryPage"] subpage_types = ["learnpath.LearningPath", "media_library.MediaLibraryPage"]
course = models.ForeignKey("course.Course", on_delete=models.PROTECT) course = models.ForeignKey("course.Course", on_delete=models.PROTECT)

View File

@ -0,0 +1,12 @@
from rest_framework import permissions
class CourseAccessPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user

View File

@ -0,0 +1,42 @@
from rest_framework.fields import SerializerMethodField
from vbv_lernwelt.core.serializer_helpers import ItWagtailBaseSerializer, \
get_it_serializer_class
class CourseBaseSerializer(ItWagtailBaseSerializer):
course = SerializerMethodField()
course_category = SerializerMethodField()
circles = SerializerMethodField()
meta_fields = []
def get_course(self, obj):
course = obj.get_course()
if course:
from vbv_lernwelt.course.serializers import CourseSerializer
return CourseSerializer(course).data
return ""
def get_course_category(self, obj):
from vbv_lernwelt.course.serializers import CourseCategorySerializer
return CourseCategorySerializer(obj.course_category).data
def get_circles(self, obj):
circles = obj.get_circles()
if circles:
return [
{"id": c.id, "title": c.title, "translation_key": c.translation_key}
for c in circles
]
return []
def get_course_serializer_class(model, field_names=None, base_field_names=None):
return get_it_serializer_class(
model, field_names, base_field_names, CourseBaseSerializer
)

View File

@ -1,6 +1,10 @@
from rest_framework import serializers from rest_framework import serializers
from vbv_lernwelt.course.models import Course, CourseCategory, CourseCompletion from vbv_lernwelt.course.models import (
Course,
CourseCategory,
CourseCompletion,
)
class CourseSerializer(serializers.ModelSerializer): class CourseSerializer(serializers.ModelSerializer):

View File

@ -8,8 +8,7 @@ from wagtail.images.blocks import ImageChooserBlock
from wagtail.models import Page from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class from vbv_lernwelt.course.models import CoursePage, CourseBasePage
from vbv_lernwelt.course.models import CoursePage
from vbv_lernwelt.learnpath.models_learning_unit_content import ( from vbv_lernwelt.learnpath.models_learning_unit_content import (
AssignmentBlock, AssignmentBlock,
BookBlock, BookBlock,
@ -25,7 +24,8 @@ from vbv_lernwelt.learnpath.models_learning_unit_content import (
) )
class LearningPath(Page): class LearningPath(CourseBasePage):
serialize_field_names = ["children", "course"]
content_panels = Page.content_panels content_panels = Page.content_panels
subpage_types = ["learnpath.Circle", "learnpath.Topic"] subpage_types = ["learnpath.Circle", "learnpath.Topic"]
parent_page_types = ["course.CoursePage"] parent_page_types = ["course.CoursePage"]
@ -45,19 +45,10 @@ class LearningPath(Page):
def get_frontend_url(self): def get_frontend_url(self):
return f"/learn/{self.slug}" return f"/learn/{self.slug}"
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
[
"children",
"course",
],
)
class Topic(CourseBasePage):
serialize_field_names = ["is_visible"]
class Topic(Page):
# title = models.TextField(default='')
is_visible = models.BooleanField(default=True) is_visible = models.BooleanField(default=True)
parent_page_types = ["learnpath.LearningPath"] parent_page_types = ["learnpath.LearningPath"]
@ -67,27 +58,10 @@ class Topic(Page):
FieldPanel("is_visible"), FieldPanel("is_visible"),
] ]
# content_panels = Page.content_panels + [
# FieldPanel('is_visible', classname="full"),
# PageChooserPanel('learning_path', 'learnpath.LearningPath'),
# ]
# parent_page_types = ['learnpath.LearningPath']
# subpage_types = ['learnpath.Circle']
def full_clean(self, *args, **kwargs): def full_clean(self, *args, **kwargs):
self.slug = find_slug_with_parent_prefix(self, "topic") self.slug = find_slug_with_parent_prefix(self, "topic")
super(Topic, self).full_clean(*args, **kwargs) super(Topic, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
field_names=[
"is_visible",
],
)
def get_admin_display_title(self): def get_admin_display_title(self):
return f"Thema: {self.draft_title}" return f"Thema: {self.draft_title}"
@ -109,7 +83,7 @@ class PersonBlock(blocks.StructBlock):
icon = "user" icon = "user"
class Circle(Page): class Circle(CourseBasePage):
parent_page_types = ["learnpath.LearningPath"] parent_page_types = ["learnpath.LearningPath"]
subpage_types = [ subpage_types = [
"learnpath.LearningSequence", "learnpath.LearningSequence",
@ -117,6 +91,16 @@ class Circle(Page):
"learnpath.LearningContent", "learnpath.LearningContent",
] ]
serialize_field_names = [
"children",
"description",
"goal_description",
"goals",
"job_situation_description",
"job_situations",
"experts",
]
description = models.TextField(default="", blank=True) description = models.TextField(default="", blank=True)
goal_description = models.TextField(default="", blank=True) goal_description = models.TextField(default="", blank=True)
@ -148,21 +132,6 @@ class Circle(Page):
FieldPanel("experts"), FieldPanel("experts"),
] ]
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
field_names=[
"children",
"description",
"goal_description",
"goals",
"job_situation_description",
"job_situations",
"experts",
],
)
def get_frontend_url(self): def get_frontend_url(self):
short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "") short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "")
return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}" return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}"
@ -178,7 +147,9 @@ class Circle(Page):
return f"{self.title}" return f"{self.title}"
class LearningSequence(Page): class LearningSequence(CourseBasePage):
serialize_field_names = ["icon"]
parent_page_types = ["learnpath.Circle"] parent_page_types = ["learnpath.Circle"]
subpage_types = [] subpage_types = []
@ -194,10 +165,6 @@ class LearningSequence(Page):
def __str__(self): def __str__(self):
return f"{self.title}" return f"{self.title}"
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(cls, field_names=["icon"])
def get_admin_display_title(self): def get_admin_display_title(self):
return f"{self.icon} {self.draft_title}" return f"{self.icon} {self.draft_title}"
@ -220,7 +187,7 @@ class LearningSequence(Page):
return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}" return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}"
class LearningUnit(Page): class LearningUnit(CourseBasePage):
parent_page_types = ["learnpath.Circle"] parent_page_types = ["learnpath.Circle"]
subpage_types = [] subpage_types = []
course_category = models.ForeignKey( course_category = models.ForeignKey(
@ -277,7 +244,12 @@ class LearningUnit(Page):
return f'<span style="font-weight: 700; font-size: 20px;">{self.draft_title}</span>' return f'<span style="font-weight: 700; font-size: 20px;">{self.draft_title}</span>'
class LearningContent(Page): class LearningContent(CourseBasePage):
serialize_field_names = [
"minutes",
"contents",
]
parent_page_types = ["learnpath.Circle"] parent_page_types = ["learnpath.Circle"]
subpage_types = [] subpage_types = []
@ -335,19 +307,6 @@ class LearningContent(Page):
self.slug = find_slug_with_parent_prefix(self, "lc") self.slug = find_slug_with_parent_prefix(self, "lc")
super(LearningContent, self).full_clean(*args, **kwargs) super(LearningContent, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
field_names=[
"minutes",
"contents",
],
)
def __str__(self):
return f"{self.title}"
def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None): def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None):
parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug

View File

@ -3,14 +3,14 @@ from rest_framework.fields import SerializerMethodField
from vbv_lernwelt.competence.serializers import ( from vbv_lernwelt.competence.serializers import (
PerformanceCriteriaLearningPathSerializer, PerformanceCriteriaLearningPathSerializer,
) )
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
from vbv_lernwelt.learnpath.models import LearningUnit from vbv_lernwelt.learnpath.models import LearningUnit
class LearningUnitSerializer( class LearningUnitSerializer(
get_it_serializer_class( get_course_serializer_class(
LearningUnit, LearningUnit,
[ field_names=[
"evaluate_url", "evaluate_url",
"course_category", "course_category",
"children", "children",
@ -30,9 +30,9 @@ class LearningUnitSerializer(
class LearningUnitPerformanceCriteriaSerializer( class LearningUnitPerformanceCriteriaSerializer(
get_it_serializer_class( get_course_serializer_class(
LearningUnit, LearningUnit,
[ field_names=[
"evaluate_url", "evaluate_url",
"course_category", "course_category",
], ],

View File

@ -7,11 +7,13 @@ from wagtail.fields import StreamField
from wagtail.models import Page from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class from vbv_lernwelt.course.models import CourseBasePage
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
class MediaLibraryPage(Page): class MediaLibraryPage(CourseBasePage):
serialize_field_names = ["course", "children"]
parent_page_types = ["course.CoursePage"] parent_page_types = ["course.CoursePage"]
subpage_types = ["media_library.MediaCategoryPage"] subpage_types = ["media_library.MediaCategoryPage"]
@ -28,22 +30,23 @@ class MediaLibraryPage(Page):
def get_frontend_url(self): def get_frontend_url(self):
return f"/media/{self.slug}" return f"/media/{self.slug}"
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
[
"course",
"children",
],
)
class MediaCategoryPage(CourseBasePage):
class MediaCategoryPage(Page):
""" """
Handlungsfeld. zB. Fahrzeug Handlungsfeld. zB. Fahrzeug
""" """
serialize_field_names = [
"course_category",
"introduction_text",
"overview_icon",
"detail_image",
"description_title",
"description_text",
"items",
"body",
]
course_category = models.ForeignKey( course_category = models.ForeignKey(
"course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True "course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True
) )
@ -89,22 +92,6 @@ class MediaCategoryPage(Page):
short_slug = self.slug.replace(f"{self.get_parent().slug}-cat-", "") short_slug = self.slug.replace(f"{self.get_parent().slug}-cat-", "")
return f"{self.get_parent().specific.get_frontend_url()}/category/{short_slug}" return f"{self.get_parent().specific.get_frontend_url()}/category/{short_slug}"
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
cls,
field_names=[
"course_category",
"introduction_text",
"overview_icon",
"detail_image",
"description_title",
"description_text",
"items",
"body",
],
)
class LibraryDocument(AbstractDocument): class LibraryDocument(AbstractDocument):
# Todo: check https://filepreviews.io/ # Todo: check https://filepreviews.io/