from django.db.models import Q
from rest_framework import viewsets
from rest_framework.response import Response

from backend.apps.archive.models import AudioMaterial, ImageMaterial, VideoMaterial

from . import serializers


class BaseMaterialViewSet(viewsets.ReadOnlyModelViewSet):
    def get_common_search_lookup(self, common_search_value):
        lookup = Q()
        for field in self.common_search_fields:
            lookup |= Q(**{f"{field}__icontains": common_search_value})
        return lookup

    def get_queryset(self):
        queryset = super().get_queryset()
        common_search_query = self.request.GET.get("common_search_query")
        query_lookup = self.get_common_search_lookup(common_search_query)
        return queryset.filter(query_lookup)

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(self.queryset, many=True)
        return self.get_paginated_response(serializer.data) if page is not None else Response(serializer.data)


class AudioMaterialViewSet(BaseMaterialViewSet):
    queryset = AudioMaterial.objects.all()
    serializer_class = serializers.AudioMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "title"]


class ImageMaterialViewSet(BaseMaterialViewSet):
    queryset = ImageMaterial.objects.all()
    serializer_class = serializers.ImageMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "author"]


class VideoMaterialViewSet(BaseMaterialViewSet):
    queryset = VideoMaterial.objects.all()
    serializer_class = serializers.VideoMaterialListSerializer
    common_search_fields = ["keywords", "annotation", "persons", "title", "description"]
<script setup>
import { onBeforeMount, ref } from 'vue';
import { getRecordList, getOptions } from '@/api/default_api_promises.js';
import { useRoute, useRouter } from 'vue-router';
import Paginator from '@/components/common/Paginator.vue';
import DynamicDataList from '@/components/DynamicDataList.vue';

let route = useRoute();
let router = useRouter();

let currentTableTab = ref(route.query.material_type || 'audio');

let items = ref([]);
let totalCount = ref(0);
let pageCount = ref(0);

function getItems() {
  let urlSearchParams = new URLSearchParams(route.query);
  getRecordList({ url: `/api/${currentTableTab.value}-materials/` })({ params: urlSearchParams })
    .then(response => {
      totalCount.value = response.data.count;
      items.value = response.data.results;
      pageCount.value = Math.ceil(totalCount.value / route.query.page_size);
    })
  getOptions({ url: `/api/${currentTableTab.value}-materials/` })({ params: urlSearchParams })
    .then(response => {
      console.log(response)
    })
}

function updateTab() {
  let query = { ...route.query, material_type: currentTableTab.value };
  router.push({ name: route.name, query: query, params: { ...route.params } });
}

onBeforeMount(getItems);
</script>

<template>
  <v-tabs
    v-model="currentTableTab"
    align-tabs="center"
    color="primary-blue"
    @update:model-value="updateTab"
  >
    <v-tab value="image">Фото</v-tab>
    <v-tab value="audio">Фоно</v-tab>
    <v-tab value="video">Видео</v-tab>
  </v-tabs>
  <DynamicDataList :items="items" :materialType="currentTableTab"/>
  <Paginator v-if="items.length" :page-count="pageCount"/>
</template>

<style scoped lang="scss">
/* Add your styles here */
</style>
<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>"""
Django settings for backend project.

Generated by 'django-admin startproject' using Django 5.1.

For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""

from pathlib import Path

from environs import Env

# read .env file into env variable
# for more info https://github.com/sloria/environs
env = Env()
env.read_env()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

DEBUG = env.bool("DEBUG")
SECRET_KEY = env("SECRET_KEY")

ALLOWED_HOSTS = ["*"]

# CORS
CORS_ALLOW_ALL_ORIGINS = True

# Application definition
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # project apps
    "backend.apps.core",
    "backend.apps.archive",
    # third-party apps
    "rest_framework",
    "corsheaders",
    # internal apps
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "backend.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "backend.wsgi.application"


# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": env("DB_NAME", "---"),
        "USER": env("DB_USER", "---"),
        "PASSWORD": env("DB_PASSWORD", "---"),
        "HOST": env("DB_HOST", "---"),
        "PORT": "5432",
    }
}

REST_FRAMEWORK = {
    "DATE_FORMAT": "%d.%m.%Y",
    "DATE_INPUT_FORMATS": ["%d.%m.%Y"],
    "DATETIME_FORMAT": "%d.%m.%Y %H:%M:%S",
    "DATETIME_INPUT_FORMAT": "%Y-%m-%dT%H:%M:%S",
    "DEFAULT_PAGINATION_CLASS": "backend.apps.core.pagination.PageNumberPaginationWithAll",
    "DEFAULT_PARSER_CLASSES": [
        "rest_framework.parsers.JSONParser",
        "rest_framework.parsers.FormParser",
        "rest_framework.parsers.MultiPartParser",
    ],
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"],
}

# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"

USE_I18N = True
USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/"

# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Minio
MINIO_ENDPOINT = "s3.docker.wiseweb.by"
MINIO_USE_HTTPS = False
MINIO_ACCESS_KEY = env.str("MINIO_ACCESS_KEY")
MINIO_SECRET_KEY = env.str("MINIO_SECRET_KEY")
MINIO_PRIVATE_BUCKETS = [
    "archive-kffd",
]
MINIO_POLICY_HOOKS: list[tuple[str, dict]] = []

from contextlib import suppress

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', 'PUT', and 'POST' methods when 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 'GET', 'PUT', 'POST'
        for method in {'GET', 'PUT', 'POST'} & 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
как мне напрямую во вью или в каком-либо другом файле указать чтобы использовался файл метадаты backend/apps/archive/metadata.py вместо дефолтного файла метадаты