django-parler / django-parler-rest

Translatable model support for django-rest-framework
Apache License 2.0
59 stars 16 forks source link

OPTIONS request with TranslatedFieldsField #34

Open MehdiDRISSIB opened 3 years ago

MehdiDRISSIB commented 3 years ago

Hello,

Is there is any way to show all details of each field in TranslatedFieldsField in serializer in OPTIONS request ?

Currently, when calling OPTIONS request, I have :

{
    "name": "Dispute List",
    "description": "",
    "renders": [
        "application/json",
        "text/html"
    ],
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "actions": {
        "GET": {
            "id": {
                "type": "integer",
                "required": false,
                "read_only": true,
                "label": "ID"
            },
            "translations": {
                "type": "field",
                "required": true,
                "read_only": false,
                "label": "Translations"
            },
            "created_on": {
                "type": "datetime",
                "required": false,
                "read_only": true,
                "label": "Created on"
            }, .....

But I want also all the description of each field in 'translations' (TranslatedFieldsField)

Many thanks for your answers

MehdiDRISSIB commented 3 years ago

Hello,

After some works, I just created this class to show Options request with all fields in translations. I override get_serializer_info to show the fields in the related_model

import types
from collections import OrderedDict
from rest_framework.metadata import SimpleMetadata
from django.core.exceptions import PermissionDenied
from django.http import Http404
from rest_framework import exceptions, serializers
from rest_framework.request import clone_request
from parler.models import TranslatedFieldsModelBase
from parler_rest.fields import TranslatedFieldsField
from typing import Type
from django.db import models

#@lru_cache
def model_serializer(model: Type[models.Model]) -> Type[serializers.ModelSerializer]:
    meta_class = types.new_class("Meta")
    setattr(meta_class, "model", model)
    setattr(meta_class, "fields", "__all__")
    result = types.new_class(
        model.__name__ + "Serializer", (serializers.ModelSerializer,), {}
    )
    setattr(result, "Meta", meta_class)
    return result

class Metadata(SimpleMetadata):

    def determine_actions(self, request, view):
        actions = {}
        for method in {'PUT', 'POST', 'GET'} & set(view.allowed_methods):
            view.request = clone_request(request, method)
            try:
                # Test global permissions
                if hasattr(view, 'check_permissions'):
                    view.check_permissions(view.request)
                # Test object permissions
                if method == 'PUT' and hasattr(view, 'get_object'):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):
                pass
            else:
                # If user has appropriate permissions for the view, include
                # appropriate metadata about the fields that should be supplied.
                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)

            finally:
                view.request = request

        return actions
        #return super(Metadata, self).determine_actions(request, view)

    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, 'child'):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child

        serializer_info = OrderedDict()
        for field_name, field in serializer.fields.items():
            if not isinstance(field, serializers.HiddenField):
                if isinstance(field, (TranslatedFieldsField)):
                    translatable_fields = field.shared_model._parler_meta.get_all_fields()

                    for _field in serializer.Meta.model._meta.get_fields():
                        if isinstance(_field.related_model, TranslatedFieldsModelBase):
                            TranslatableModelSerializer = model_serializer(_field.related_model)
                            TranslatableSerializer = TranslatableModelSerializer()
                            for translatable_field in translatable_fields:
                                translatable_serializer_field = TranslatableSerializer.fields.get(translatable_field)
                                serializer_info.update({translatable_field: self.get_field_info(translatable_serializer_field)})
                else:
                    serializer_info.update({field_name: self.get_field_info(field)})
        return serializer_info

This class is added to REST_FRAMEWORK variable in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_METADATA_CLASS': 'apps.utils.metadata.Metadata',
    ....

I hope this will help someone