diff --git a/geonode/base/api/fields.py b/geonode/base/api/fields.py index 30bee56b73b..becfed80ad6 100644 --- a/geonode/base/api/fields.py +++ b/geonode/base/api/fields.py @@ -23,7 +23,14 @@ from rest_framework.exceptions import ParseError from dynamic_rest.fields.fields import DynamicRelationField -from geonode.base.models import RelatedIdentifierType, RelationType, RelatedIdentifier, FundingReference, Funder +from geonode.base.models import ( + RelatedIdentifierType, + RelationType, + RelatedIdentifier, + FundingReference, + Funder, + HierarchicalKeyword, +) class RelatedIdentifierDynamicRelationField(DynamicRelationField): @@ -56,6 +63,52 @@ def to_internal_value_single(self, data, serializer): return funder[0] +class KeywordsDynamicRelationField(DynamicRelationField): + def to_internal_value_single(self, data, serializer): + """Overwrite of DynamicRelationField implementation to handle complex data structure initialization + + Args: + data (Union[str, Dict]}): serialized or deserialized data from http calls (POST, GET ...), + if content-type application/json is used, data shows up as dict + serializer (DynamicModelSerializer): Serializer for the given data + + Raises: + ValidationError: raised when requested data does not exist + + django.db.models.QuerySet: return QuerySet object of the request or set data + """ + + related_model = serializer.Meta.model + try: + if isinstance(data, str): + data = json.loads(data) + except ValueError: + return super().to_internal_value_single(data, serializer) + + if not isinstance(data, dict): + return super().to_internal_value_single(data, serializer) + + def __set_full_keyword__(d): + if "name" not in d: + raise ValidationError('No "name" object found for given keyword ...') + if "slug" not in d: + d["slug"] = d["name"] + if "depth" not in d: + d["depth"] = 1 + if "path" not in d: + d["path"] = d["name"] + return d + + data = __set_full_keyword__(data) + keyword = HierarchicalKeyword.objects.filter(name=data["name"]).first() + if keyword is None: + keyword = HierarchicalKeyword.objects.create( + name=data["name"], slug=data["slug"], depth=data["depth"], path=data["path"] + ) + keyword.save() + return keyword + + class ComplexDynamicRelationField(DynamicRelationField): def to_internal_value_single(self, data, serializer): """Overwrite of DynamicRelationField implementation to handle complex data structure initialization diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py index 7e757c5f2ec..dd0871597e1 100644 --- a/geonode/base/api/serializers.py +++ b/geonode/base/api/serializers.py @@ -72,6 +72,7 @@ ComplexDynamicRelationField, RelatedIdentifierDynamicRelationField, FundersDynamicRelationField, + KeywordsDynamicRelationField, ) from geonode.layers.utils import get_dataset_download_handlers, get_default_dataset_download_handler from geonode.utils import build_absolute_uri @@ -703,7 +704,7 @@ def __init__(self, *args, **kwargs): self.fields["sourcetype"] = serializers.CharField(read_only=True) self.fields["embed_url"] = EmbedUrlField(required=False) self.fields["thumbnail_url"] = ThumbnailUrlField(read_only=True) - self.fields["keywords"] = ComplexDynamicRelationField( + self.fields["keywords"] = KeywordsDynamicRelationField( SimpleHierarchicalKeywordSerializer, embed=False, many=True ) self.fields["tkeywords"] = ComplexDynamicRelationField(SimpleThesaurusKeywordSerializer, embed=False, many=True) @@ -882,7 +883,7 @@ def save(self, **kwargs): instance = super().save(**kwargs) if keywords is not None: instance.keywords.clear() - [instance.keywords.add(keyword) for keyword in keywords] + [instance.keywords.add(keyword.name) for keyword in keywords] if extent and instance.get_real_instance()._meta.model in api_bbox_settable_resource_models: srid = extent.get("srid", "EPSG:4326") coords = extent.get("coords")