Commit e4f880fd authored by Christophe Henry's avatar Christophe Henry

feat: Support GenericForeignKey support in ser/deser

parent 48c16aeb
Pipeline #7138 passed with stage
in 1 minute and 21 seconds
import requests
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from rest_framework import fields
from rest_framework.exceptions import ValidationError
class IdURLField (fields.URLField):
......@@ -17,3 +20,27 @@ class IdURLField (fields.URLField):
class LDPUrlField (models.URLField):
class LDPGenericForeignKeyField(fields.URLField):
def __init__(self, **kwargs):
# This is a virtual field we don't want validation
def to_internal_value(self, data):
gfk: GenericForeignKey = getattr(self.root.Meta.model, self.field_name)
from djangoldp.models import Model
container, resolve_id = Model.resolve(data)
if resolve_id is None:
raise ValidationError("'target' with id {} couldn't be found".format(data))
return {
gfk.ct_field: ContentType.objects.get_for_model(container).pk
def to_representation(self, value):
return {'@id': str(value.urlid)}
from collections import OrderedDict, Mapping, Iterable
from inspect import isclass
from typing import Any
from urllib import parse
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404
......@@ -21,7 +24,7 @@ from rest_framework.utils import model_meta
from rest_framework.utils.field_mapping import get_nested_relation_kwargs
from rest_framework.utils.serializer_helpers import ReturnDict
from djangoldp.fields import LDPUrlField, IdURLField
from djangoldp.fields import LDPUrlField, IdURLField, LDPGenericForeignKeyField
from djangoldp.models import Model
from djangoldp.permissions import LDPPermissions
......@@ -249,6 +252,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
serializer_related_field = JsonLdRelatedField
serializer_url_field = JsonLdIdentityField
ModelSerializer.serializer_field_mapping[LDPUrlField] = IdURLField
ModelSerializer.serializer_field_mapping[GenericForeignKey] = LDPGenericForeignKeyField
def get_default_field_names(self, declared_fields, model_info):
......@@ -285,8 +289,16 @@ class LDPSerializer(HyperlinkedModelSerializer):
return data
def build_field(self, field_name, info, model_class, nested_depth):
return super().build_field(field_name, info, model_class, nested_depth)
def get_fields(self):
"""We need to handle the spacial case of `GenericForeingKey here"""
fields = super().get_fields()
for name, item in self._get_model_generic_foreign_keys():
fields.pop(item.ct_field, None)
fields.pop(item.fk_field, None)
fields[name] = LDPGenericForeignKeyField()
return fields
def build_property_field(self, field_name, model_class):
class JSonLDPropertyField(ReadOnlyField):
......@@ -472,7 +484,18 @@ class LDPSerializer(HyperlinkedModelSerializer):
if user_case:
data['username'] = 'external'
# Handle sepcial case of GenericForeignKeys here
gfk_names = [name for name, _ in self._get_model_generic_foreign_keys()]
ret = super().to_internal_value(data)
# Let's flatten the dict returned from GenericForeignKey serialization
for name in gfk_names:
gfk_actual_fields = ret.pop(name, None)
if isinstance(gfk_actual_fields, dict):
for gfk_actual_field_name, item in gfk_actual_fields.items():
ret[gfk_actual_field_name] = item
if user_case:
ret['username'] = data['@id']
return ret
......@@ -603,6 +626,12 @@ class LDPSerializer(HyperlinkedModelSerializer):
if sub_inst is None:
sub_inst = self.internal_create(field_dict, field_model)
validated_data[field_name] = sub_inst
# Handle GenericForeignKey case
for name, _ in self._get_model_generic_foreign_keys():
ct_field = getattr(self.Meta.model, name).ct_field
validated_data[ct_field] = ContentType.objects.get(pk=validated_data[ct_field])
return validated_data
def update_dict_value(self, attr, instance, value):
......@@ -708,3 +737,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
except field_model.DoesNotExist:
saved_item = self.internal_create(validated_data=item, model=field_model)
return saved_item
def _get_model_generic_foreign_keys(self):
model_fields = dict(vars(self.Meta.model))
return [(name, item) for name, item in model_fields.items() if isinstance(item, GenericForeignKey)]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment