from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_str

from rest_framework import exceptions, serializers
from rest_framework.metadata import SimpleMetadata
from rest_framework.request import clone_request
from rest_framework.utils.field_mapping import ClassLookupDict


class CustomMetadata(SimpleMetadata):
    """
    A custom metadata class that extends SimpleMetadata to include
    'GET' method alongside 'PUT' and 'POST' for determining actions.
    """

    label_lookup = ClassLookupDict({
        serializers.Field: 'field',
        serializers.BooleanField: 'boolean',
        serializers.CharField: 'string',
        serializers.UUIDField: 'string',
        serializers.URLField: 'url',
        serializers.EmailField: 'email',
        serializers.RegexField: 'regex',
        serializers.SlugField: 'slug',
        serializers.IntegerField: 'integer',
        serializers.FloatField: 'float',
        serializers.DecimalField: 'decimal',
        serializers.DateField: 'date',
        serializers.DateTimeField: 'datetime',
        serializers.TimeField: 'time',
        serializers.DurationField: 'duration',
        serializers.ChoiceField: 'choice',
        serializers.MultipleChoiceField: 'multiple choice',
        serializers.FileField: 'file upload',
        serializers.ImageField: 'image upload',
        serializers.ListField: 'list',
        serializers.DictField: 'nested object',
        serializers.Serializer: 'nested object',
    })

    def determine_metadata(self, request, view):
        """
        Return a dictionary of metadata about the view, including actions.
        """
        metadata = {
            "name": view.get_view_name(),
            "description": view.get_view_description(),
            "renders": [renderer.media_type for renderer in view.renderer_classes],
            "parses": [parser.media_type for parser in view.parser_classes],
        }
        if hasattr(view, 'get_serializer'):
            actions = self.determine_actions(request, view)
            if actions:
                metadata['actions'] = actions
        return metadata

    def determine_actions(self, request, view):
        """
        Determine available actions (GET, PUT, POST) and their associated fields.
        """
        actions = {}
        # Include 'PUT', 'POST', and 'GET'
        for method in {'PUT', 'POST', 'GET'} & set(view.allowed_methods):
            # Clone the request for the specific method
            view.request = clone_request(request, method)
            try:
                # Check global permissions
                if hasattr(view, 'check_permissions'):
                    view.check_permissions(view.request)
                # Check object permissions for 'PUT'
                if method == 'PUT' and hasattr(view, 'get_object'):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):
                # If permission checks fail, skip adding metadata for this method
                pass
            else:
                # If permissions pass, retrieve the serializer and its metadata
                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)
            finally:
                # Restore the original request
                view.request = request

        return actions

    def get_serializer_info(self, serializer):
        """
        Extract metadata from the serializer's fields.
        """
        if hasattr(serializer, 'child'):
            # Handle ListSerializer by examining the child serializer
            serializer = serializer.child
        return {
            field_name: self.get_field_info(field)
            for field_name, field in serializer.fields.items()
            if not isinstance(field, serializers.HiddenField)
        }

    def get_field_info(self, field):
        """
        Extract metadata from an individual serializer field.
        """
        field_info = {
            "type": self.label_lookup[field],
            "required": getattr(field, "required", False),
        }

        # Attributes to include if they are present
        attrs = [
            'read_only', 'label', 'help_text',
            'min_length', 'max_length',
            'min_value', 'max_value',
            'max_digits', 'decimal_places'
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_str(value, strings_only=True)

        # Handle nested serializers or child fields
        if hasattr(field, 'child'):
            field_info['child'] = self.get_field_info(field.child)
        elif hasattr(field, 'fields'):
            field_info['children'] = self.get_serializer_info(field)

        # Include choices for relevant fields
        if (not field_info.get('read_only') and
            not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and
                hasattr(field, 'choices')):
            field_info['choices'] = [
                {
                    'value': choice_value,
                    'display_name': force_str(choice_name, strings_only=True)
                }
                for choice_value, choice_name in field.choices.items()
            ]

        return field_info
<template>
  <v-data-iterator :items="items" class="d-flex justify-center">
    <template v-slot:default="{ items }">
      <template v-for="(item, index) in items" :key="index">
        <div class="item">
          <div class="item-icon">
            <v-icon :size="110" color="primary-violet" :icon="iconMap[materialType] || 'mdi-file-document-outline'"></v-icon>
          </div>
          <div class="item-content">
            <div v-for="(value, key) in item.raw" :key="key" class="item-field">
              <span class="field-label">{{ key }}:</span>
              <span class="field-value">{{ value }}</span>
            </div>
          </div>
        </div>
      </template>
    </template>
  </v-data-iterator>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const props = defineProps({
  items: Array,
  materialType: String
});

const fields = ref({});

const iconMap = {
  audio: 'mdi-volume-high',
  photo: 'mdi-image-outline',
  video: 'mdi-video-outline'
};

const fetchFieldsMetadata = () => {
  axios.options(`/api/materials/${props.materialType}/`)
    .then(({ data }) => {
      fields.value = data;
    })
    .catch(error => {
      console.error('Error fetching field metadata:', error);
    });
};

onMounted(fetchFieldsMetadata);
</script>

<style scoped lang="scss">
.item {
  width: 900px;
  padding: 15px;
  margin-top: 15px;
  margin-bottom: 15px;
  border: 1px solid darkgray;
  border-radius: 8px;
  display: flex;
  overflow: hidden;
}

.item-icon {
  flex-shrink: 0;
  width: 110px;
}

.item-content {
  flex-grow: 1;
  margin-left: 20px;
  overflow: hidden;
}

.item-field {
  display: flex;
  margin-bottom: 8px;
}

.field-label {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: bold;
  color: var(--color-primary);
  margin-right: 12px;
}

.field-value {
  font-size: 12px;
  font-weight: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_APP_API_URL,
  headers: {
    'Content-Type': 'application/json',
  }
})

export default api;
import api from "@/plugins/axios.js";

function recordBase(method, options) {
  const defaultAxios = {
    method: method
  }
  return function (queryParams) {
    return api({ ...defaultAxios, ...options, ...queryParams })
  };
}

export function getRecordList(options) {
  return recordBase("get", options)
}

export function getRecordDetail(options) {
  return recordBase("get", options)
}

export function getOptions(options) {
  return recordBase("options", options)
}
Object { data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, request: XMLHttpRequest }
​
config: Object { timeout: 0, xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", … }
​
data: Object { name: "Video Material List", description: "", renders: (2) […], … }
​
headers: Object { "content-length": "178", "content-type": "application/json" }
​
request: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​
status: 200
​
statusText: "OK" ИЗМЕНИ КОД ЧТОБЫ ОТДАВАЛАСЬ НЕ ВСЯ ИНФОРМАЦИЯ В МТЕАДАТУ А ТОЛЬКО НАПРИМЕР 
data: Object { name: "Video Material List", description: "", renders: (2) […], … }
​ (В ТЕХ ФАЙЛАХ, КОТОРЫЕ ТЫ МЕНЯЕШЬ ПИШИ ПОЛНЫЙ ИЗМЕНЕННЫЙ КОД ФАЙЛА А НЕ ТОЛЬКО НЕКОТОРЫЕ ЧАСТИ)