updated uri to allow optional trailing slash on single resource URIs (django automatic redirects don't carry over the Authorization header that can sometimes be needed, for instance with OAuth)
import json
import uuid
from django.contrib.auth import get_user_model
from django.conf import settings
from guardian.shortcuts import assign_perm
from renkanmanager.models import Renkan, Workspace, Revision
from rest_framework import serializers
RENKAN_USER_DISPLAY_FIELD = getattr(settings, "RENKAN_USER_DISPLAY_FIELD", get_user_model().USERNAME_FIELD)
class RenkanSerializer(serializers.Serializer):
id = serializers.ReadOnlyField(source="renkan_guid")
current_revision_id = serializers.ReadOnlyField(source="current_revision_guid")
workspace_id = serializers.CharField(source="workspace_guid", required=False)
source_revision_id = serializers.CharField(source="source_revision_guid", required=False)
revision_count = serializers.ReadOnlyField()
created_by = serializers.SlugRelatedField(source="creator", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True)
last_updated_by = serializers.SerializerMethodField("get_current_revision_last_updator")
title = serializers.CharField(required=False)
content = serializers.JSONField(required=False)
creation_date = serializers.ReadOnlyField()
modification_date = serializers.SerializerMethodField("get_current_revision_modification_date")
create_new_revision = serializers.BooleanField(write_only=True, required=False) # only used for updating
# ADD ERROR HANDLING
def get_current_revision_last_updator(self, renkan):
current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid)
return getattr(current_revision.last_updated_by, RENKAN_USER_DISPLAY_FIELD)
def get_current_revision_modification_date(self, renkan):
current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid)
return current_revision.modification_date
def create(self, validated_data):
"""
Method to create a new Renkan (and its first revision)
"""
creator = validated_data.get('creator')
initial_revision = Revision.objects.create(
title = validated_data.get('title', 'Untitled renkan'),
creator = creator,
last_updated_by = creator
)
renkan = Renkan.objects.create(
current_revision_guid = initial_revision.revision_guid,
workspace_guid = validated_data.get('workspace_guid', ''),
source_revision_guid = validated_data.get('source_revision_guid', ''),
creator = creator
)
initial_revision.parent_renkan_guid = renkan.renkan_guid
initial_revision.content = validated_data.get('content', json.dumps(
{
"id": str(renkan.renkan_guid),
"title": initial_revision.title,
"description": "",
"created": str(initial_revision.creation_date),
"updated": str(initial_revision.modification_date),
"edges": [],
"nodes": [],
"users": [],
"space_id": str(renkan.workspace_guid),
"views": []
}
))
initial_revision.save()
renkan.save()
assign_perm('view_renkan', creator, renkan)
assign_perm('change_renkan', creator, renkan)
assign_perm('delete_renkan', creator, renkan)
assign_perm('view_revision', creator, initial_revision)
assign_perm('delete_revision', creator, initial_revision)
return renkan
def update(self, renkan, validated_data):
"""
Method to update a Renkan object. Creates a new revision if needed
"""
updator = validated_data.get('updator')
current_revision = Revision.objects.get(revision_guid=renkan.current_revision_guid)
if validated_data.get("create_new_revision", False):
revision_to_update = Revision.objects.create()
revision_to_update.parent_renkan_guid = renkan.renkan_guid
revision_to_update.creator = updator
renkan.current_revision_guid = revision_to_update.revision_guid
else:
revision_to_update = current_revision
revision_to_update.title = validated_data.get('title', current_revision.title)
revision_to_update.content = validated_data.get('content', current_revision.content)
revision_to_update.last_updated_by = updator
revision_to_update.save()
if not updator.has_perm('view_revision', revision_to_update):
assign_perm('view_revision', updator, revision_to_update)
assign_perm('delete_revision', updator, revision_to_update)
renkan.save()
return renkan
def validate_workspace_id(self, value):
if self.instance is not None:
raise serializers.ValidationError("You cannot update workspace_guid")
return value
def validate_source_revision_id(self, value):
if self.instance is not None:
raise serializers.ValidationError("You cannot update source_revision_guid")
return value
def validate_content(self, value):
try:
json.loads(value)
except ValueError:
raise serializers.ValidationError("Content format is not a JSON-serializable")
loaded_json = json.loads(value)
if (not "nodes" in loaded_json):
raise serializers.ValidationError("Content requires a 'nodes' entry")
if (not "edges" in loaded_json):
raise serializers.ValidationError("Content requires a 'edges' entry")
if (not "views" in loaded_json):
raise serializers.ValidationError("Content requires a 'views' entry")
return value
class RevisionSerializer(serializers.Serializer):
id = serializers.ReadOnlyField(source="revision_guid")
parent_renkan_id = serializers.ReadOnlyField(source="parent_renkan_guid")
workspace_id = serializers.SerializerMethodField("get_related_workspace_guid")
title = serializers.ReadOnlyField()
content = serializers.JSONField(read_only=True)
renkan_created_by = serializers.SerializerMethodField("get_related_renkan_creator")
renkan_creation_date = serializers.SerializerMethodField("get_related_renkan_creation_date")
revision_created_by = serializers.SlugRelatedField(source="creator", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True)
revision_last_updated_by = serializers.StringRelatedField(source="last_updated_by")
revision_modification_date = serializers.ReadOnlyField(source="modification_date")
def get_related_workspace_guid(self, revision):
parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
return parent_renkan.workspace_guid
def get_related_renkan_creator(self, revision):
parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
return getattr(parent_renkan.creator, RENKAN_USER_DISPLAY_FIELD)
def get_related_renkan_creation_date(self, revision):
parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
return parent_renkan.creation_date
class WorkspaceSerializer(serializers.Serializer):
id = serializers.ReadOnlyField(source="workspace_guid")
workspace_created_by = serializers.SlugRelatedField(source="creator", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True)
creation_date = serializers.ReadOnlyField()
renkan_count = serializers.ReadOnlyField()
title = serializers.CharField()
def create(self, validated_data):
creator = validated_data.get('creator')
workspace = Workspace.objects.create()
workspace.title = validated_data.get('title', '')
workspace.creator = creator
workspace.save()
assign_perm('view_workspace', creator, workspace)
assign_perm('change_workspace', creator, workspace)
assign_perm('delete_workspace', creator, workspace)
return workspace
def update(self, workspace, validated_data):
workspace.title = validated_data.get('title', '')
workspace.save()
return workspace