I will send you two codes. the first one is code from another project, which is a reference for me, and the second one is code from my project. i need you to adopt the technologies used in the code of the other project to implement dynamic addition of database field names when search results are displayed. i already have a working example: id:240971
archive_number:CD00004-058
title:Muz. G. Puxta. Suites on themes of modern Belarusian songs.
annotation: “Uzyshaw svezel month”.
performer:domra sextet
creation_date:1967
language:undefined, but the field names here are in English, and I want them in Russian. my project file models.py (which I will also send you) contains lines of code like annotation = models.TextField(“Аннотация”, blank=True). I need this word “Аннотация” to be displayed, not annotation. it is also very important that you do not make changes to models.py and serializers.py in the code of my project. you can only make changes to views.py and the frontend as needed. my code: from django.db.models import Q
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status
from backend.apps.archive.models import AudioMaterial, ImageMaterial, VideoMaterial
from . import serializers
from django.utils.text import capfirst
from rest_framework.metadata import SimpleMetadata
import logging

from django.core.exceptions import FieldDoesNotExist

# Set up logging
logger = logging.getLogger(__name__)

class SerializerFieldsMetadata(SimpleMetadata):
    """
    Generates metadata for fields, ensuring labels use model verbose_name.
    """

    def get_field_info(self, field):
        field_info = super().get_field_info(field)
        label = field.label

        if not label:
            model = None
            root = field.root
            if root is not None and hasattr(root, 'Meta') and hasattr(root.Meta, 'model'):
                model = root.Meta.model
            if model and field.field_name:
                try:
                    model_field = model._meta.get_field(field.field_name)
                    label = model_field.verbose_name
                except FieldDoesNotExist:
                    label = field.field_name.replace('_', ' ').capitalize()
            else:
                label = field.field_name.replace('_', ' ').capitalize()

        field_info['label'] = capfirst(label)
        field_info["type"] = field.__class__.__name__

        if isinstance(field, serializers.ChoiceField):
            field_info["choices"] = [
                {"value": key, "display_name": value}
                for key, value in field.choices.items()
            ]

        return field_info

    def determine_metadata(self, request, view):
        metadata = super().determine_metadata(request, view)
        queryset = view.get_queryset()
        model = queryset.model
        metadata["verbose_name"] = model._meta.verbose_name
        metadata["verbose_name_plural"] = model._meta.verbose_name_plural
        return metadata

class BaseMaterialViewSet(viewsets.ReadOnlyModelViewSet):
    """Base ViewSet for all media materials."""
    metadata_class = SerializerFieldsMetadata

    def get_queryset(self):
        queryset = super().get_queryset()
        search_query = self.request.query_params.get("common_search_query")
        logger.debug(f"Search query: {search_query}")

        logger.debug(f"Initial queryset count: {queryset.count()}")

        if search_query:
            query_lookup = Q()
            for field in self.common_search_fields:
                query_lookup |= Q(**{f"{field}__icontains": search_query})
            queryset = queryset.filter(query_lookup)

            logger.debug(f"Filtered queryset count: {queryset.count()}")
            logger.debug(f"SQL query: {queryset.query}")

        return queryset

    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(queryset, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def options(self, request, *args, **kwargs):
        """Customized method for providing field metadata."""
        metadata = self.metadata_class().determine_metadata(request, self)
        return Response(metadata, status=status.HTTP_200_OK)

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"]import re

from rest_framework import serializers

from . import models


class ImageMaterialListSerializer(serializers.ModelSerializer):
    creation_date = serializers.SerializerMethodField()
    annotation = serializers.SerializerMethodField()

    @staticmethod
    def get_annotation(instance):
        max_tokens = 11
        if instance.annotation:
            tokens = re.findall(r"\S+", instance.annotation)
            annotation = " ".join(tokens[:max_tokens])
            annotation += " ..." if len(tokens) > max_tokens else ""
            return annotation

    @staticmethod
    def get_creation_date(instance):
        match instance.creation_date_key:
            case models.CreationDateVariant.ONLY_YEAR:
                return instance.creation_date.year
            case models.CreationDateVariant.MONTH_AND_YEAR:
                return instance.creation_date.strftime("%m.%Y")
            case models.CreationDateVariant.FULL_DATE:
                return instance.creation_date.strftime("%d.%m.%Y")

    class Meta:
        model = models.ImageMaterial
        fields = ("id", "archive_number", "annotation", "creation_date", "author", "photo_type", "color")


class AudioMaterialListSerializer(ImageMaterialListSerializer):
    class Meta:
        model = models.AudioMaterial
        fields = ("id", "archive_number", "title", "annotation", "performer", "creation_date", "language")


class VideoMaterialListSerializer(ImageMaterialListSerializer):
    class Meta:
        model = models.VideoMaterial
        fields = ("id", "archive_number", "title", "creation_date", "annotation", "roles", "language")
from django.db import models
from django_minio_backend import MinioBackend


class BaseFile(models.Model):
    id = models.BigAutoField(primary_key=True, db_column="id_object")
    file_name = models.CharField(max_length=100)

    class Meta:
        abstract = True


class VideoFile(BaseFile):
    duration = models.PositiveIntegerField("Длительность в мс", null=True)
    frame_rate = models.IntegerField("Частота кадров", null=True)
    file_path = models.FileField(storage=MinioBackend(bucket_name="archive-kffd"), upload_to="video")
    material = models.ForeignKey(
        "VideoMaterial",
        verbose_name="Видео-файл",
        related_name="media_files",
        on_delete=models.PROTECT,
        db_column="id_main",
    )

    class Meta:
        db_table = "archive_video_file"
        ordering = ("id",)


class AudioFile(BaseFile):
    duration = models.PositiveIntegerField("Длительность в мс", null=True)
    file_path = models.FileField(storage=MinioBackend(bucket_name="archive-kffd"), upload_to="audio")
    material = models.ForeignKey(
        "AudioMaterial",
        verbose_name="Аудио-файл",
        related_name="media_files",
        on_delete=models.PROTECT,
        db_column="id_main",
    )

    class Meta:
        db_table = "archive_audio_file"
        ordering = ("id",)


class ImageFile(BaseFile):
    file_path = models.FileField(storage=MinioBackend(bucket_name="archive-kffd"), upload_to="image")
    material = models.ForeignKey(
        "ImageMaterial",
        verbose_name="Фото-файл",
        related_name="media_files",
        on_delete=models.PROTECT,
        db_column="id_main",
    )

    class Meta:
        db_table = "archive_image_file"
        ordering = ("id",)


class CreationDateVariant(models.IntegerChoices):
    ONLY_YEAR = 1, "только год"
    MONTH_AND_YEAR = 3, "год и месяц"
    FULL_DATE = 7, "год, месяц и число"


class BaseMaterial(models.Model):
    id = models.BigAutoField(primary_key=True, db_column="id_main")
    keywords = models.CharField("Ключевые слова", max_length=512, blank=True)
    annotation = models.TextField("Аннотация", blank=True)
    archive_number = models.CharField(max_length=256, unique=True)
    persons = models.CharField("Персоналии", max_length=1024, blank=True)
    source = models.CharField("Источник поступления", max_length=256, blank=True)
    founder = models.CharField("Фондообразователь", max_length=256, blank=True)
    creation_date = models.DateField("Дата создания", null=True)
    creation_date_key = models.SmallIntegerField(
        help_text="Показывает какая часть даты известна в двоичном формате: "
        "001 (1) только год, "
        "011 (3) год и месяц, "
        "111 (7) год месяц и число",
        choices=CreationDateVariant.choices,
        null=True,
    )
    creation_date_comment = models.CharField("Дата создания комментарий", max_length=1024, blank=True)
    creation_place = models.CharField("Место создания", max_length=256, blank=True)
    receive_date = models.DateField("Дата поступления", null=True)

    class Meta:
        abstract = True


class AudioMaterial(BaseMaterial):
    title = models.CharField("Название", max_length=512, blank=True)
    text_author = models.CharField("Автор текста", max_length=256, blank=True)
    performer = models.CharField("Исполнитель", max_length=256, blank=True)
    music_author = models.CharField("Автор музыки", max_length=256, blank=True)
    arrangement = models.CharField("Аранжировка", max_length=256, blank=True)
    conductor = models.CharField("Дирижер", max_length=256, blank=True)
    art_director = models.CharField("Художественный руководитель", max_length=256, blank=True)
    language = models.CharField("Язык", max_length=256, blank=True)
    choir = models.CharField("Сопровождение (хор)", max_length=256, blank=True)
    orchestra = models.CharField("Сопровождение (инструменты)", max_length=256, blank=True)
    montage_sheet = models.TextField("Монтажный лист", blank=True)

    class Meta:
        verbose_name = "Фонодокумент"
        verbose_name_plural = "Фонодокументы"
        db_table = "archive_audio_material"
        ordering = ("id",)


class VideoMaterial(BaseMaterial):
    title = models.CharField("Название", max_length=512, blank=True)
    description = models.TextField("Описание сюжета", blank=True)
    montage_sheet = models.TextField("Монтажный лист", blank=True)
    roles = models.TextField("В ролях", blank=True)
    scriptwriter = models.CharField("Сценарист", max_length=512, blank=True)
    director = models.CharField("Режиссер", max_length=512, blank=True)
    cameraman = models.CharField("Оператор", max_length=512, blank=True)
    sound_engineer = models.CharField("Звукооператор", max_length=512, blank=True)
    painter = models.CharField("Художник", max_length=512, blank=True)
    composer = models.CharField("Композитор", max_length=512, blank=True)
    studio = models.CharField("Студия", max_length=512, blank=True)
    role_dubbing = models.CharField("Роли озвучивали", max_length=1024, blank=True)
    video_creators = models.CharField("Над фильмом работали", max_length=1024, blank=True)
    video_type = models.CharField("Вид документа", max_length=256, blank=True)
    color = models.CharField("Цветность", max_length=256, blank=True)
    language = models.CharField("Язык", max_length=256, blank=True)
    literature_source = models.CharField("Литературный первоисточник", max_length=512, blank=True)
    voice_type = models.CharField("Озвученность", max_length=256, blank=True)

    class Meta:
        verbose_name = "Киновидеодокумент"
        verbose_name_plural = "Киновидеодокументы"
        db_table = "archive_video_material"
        ordering = ("id",)


class ImageMaterial(BaseMaterial):
    author = models.CharField("Автор", max_length=256, blank=True)
    photo_type = models.CharField("Вид фотодокумента", max_length=256, blank=True)
    color = models.CharField("Цветность", max_length=256, blank=True)

    class Meta:
        verbose_name = "Фотодокумент"
        verbose_name_plural = "Фотодокументы"
        db_table = "archive_image_material"
        ordering = ("id",)
from rest_framework.routers import DefaultRouter

from . import views

router = DefaultRouter()
router.register(r"audio-materials", views.AudioMaterialViewSet, basename="audio-materials")
router.register(r"image-materials", views.ImageMaterialViewSet, basename="image-materials")
router.register(r"video-materials", views.VideoMaterialViewSet, basename="video-materials")

urlpatterns = [*router.urls]
<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">{{ getFieldLabel(key) }}:</span>
              <span class="field-value">{{ value }}</span>
            </div>
          </div>
        </div>
      </template>
    </template>
  </v-data-iterator>
</template>

<script setup>
import { ref, onMounted, watch } 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 }) => {
      console.log('Fetched metadata:', data);
      fields.value = data.fields || {};
    })
    .catch(error => {
      console.error('Error fetching field metadata:', error);
    });
};

const getFieldLabel = (key) => {
  const label = fields.value[key]?.label || key;
  console.log(`Field: ${key}, Label: ${label}`);
  return label;
};

watch(() => props.materialType, fetchFieldsMetadata);

onMounted(fetchFieldsMetadata);
</script>

<style scoped>
.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;
  margin-right: 12px;
}

.field-value {
  font-size: 12px;
  font-weight: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style> other project code: from contextlib import suppress

from rest_framework import serializers
from rest_framework.metadata import SimpleMetadata
from rest_framework.request import clone_request
from rest_framework.utils.field_mapping import ClassLookupDict
from ww_filters.updaters import Choice, ChoiceIn, ModelChoice, ModelChoiceIn


def get_model_verbose_name(view):
    if view.queryset is not None:
        return view.queryset.model._meta.verbose_name_plural
    elif hasattr(view, "get_queryset"):
        # TODO: надо бы как-то не вызывать get_queryset здесь
        return view.get_queryset().model._meta.verbose_name_plural


def get_filter_fields(view):
    def __update_choice():
        if isinstance(updater_value, Choice | ChoiceIn):
            choices = [{"index": index, "value": value} for index, value in updater_value.queryset]
            updater_result_data["choices"] = choices
        if isinstance(updater_value, ModelChoice | ModelChoiceIn):
            choices = [{"index": instance.pk, "value": str(instance)} for instance in updater_value.queryset]
            updater_result_data["choices"] = sorted(choices, key=lambda item: item["value"])

    if hasattr(view, "filter_form") and view.filter_form is not None:
        result_data = {}
        base_filters = (
            view.filter_form.get_base_filters(view)
            if hasattr(view.filter_form, "get_base_filters")
            else view.filter_form.base_filters
        )
        for filter_key, filter_value in base_filters.items():
            filter_key = f"{filter_key}_filter"
            result_data[filter_key] = {"title": filter_value.title, "value": filter_key, "updaters": []}
            for updater_key, updater_value in filter_value.updaters.items():
                updater_result_data = {
                    "value": updater_key,
                    "title": updater_value.title,
                    "type": updater_value.type,
                }
                __update_choice()
                result_data[filter_key]["updaters"].append(updater_result_data)
        result_data = dict(sorted(result_data.items(), key=lambda item: item[1]["title"]))
        if view.__class__.__name__ == "StatementViewSet":
            result_data = view.update_statements_filters(result_data)
        return result_data
    return {}


class SerializerFieldsMetadata(SimpleMetadata):
    # в случае использования BSelectMultiple и если мы используем дополнительный атрибут
    # в сериалайзере autocomplete_choice_kwargs
    # => задаём лимит изначально подгружаемых данных в select при загрузке формы
    _limit_multiple_choices = 30

    widget_lookup = ClassLookupDict(
        {
            serializers.SlugRelatedField: "SelectAutocomplete",
            serializers.PrimaryKeyRelatedField: "SelectAutocomplete",
            serializers.Field: "BInput",
            serializers.BooleanField: "BCheckBox",
            serializers.CharField: "BInput",
            serializers.UUIDField: "BInput",
            serializers.URLField: "BInput",
            serializers.EmailField: "BInput",
            serializers.RegexField: "BInput",
            serializers.SlugField: "SelectAutocomplete",
            serializers.IntegerField: "BNumeric",
            serializers.FloatField: "BInput",
            serializers.DecimalField: "BInput",
            serializers.DateField: "BDate",
            serializers.DateTimeField: "BDate",
            serializers.TimeField: "BInput",
            serializers.ChoiceField: "SelectAutocomplete",
            serializers.MultipleChoiceField: "BSelectMultiple",
            serializers.FileField: "BInput",
            serializers.ImageField: "BImage",
            serializers.ListField: "BInput",
            serializers.DictField: "BInput",
            serializers.Serializer: "BInput",
        }
    )

    def get_field_info(self, field):
        field_info = super().get_field_info(field)
        field_info["widget"] = self.widget_lookup[field]
        if isinstance(field, serializers.SlugRelatedField | serializers.PrimaryKeyRelatedField):
            if hasattr(field, "autocomplete_choice_kwargs"):
                field_info["choices"] = [
                    {"display_name": str(item), "value": item.id}
                    for item in field.queryset.all().order_by("-id")[: self._limit_multiple_choices]
                ]
                # для подгрузки initial в список choices в форме редактирования
                if self._view.kwargs and hasattr(instance := self._view.get_object(), field.field_name):
                    initial = getattr(instance, field.field_name)
                    if initial and str(initial) not in field_info["choices"]:
                        field_info["choices"].append({"display_name": str(initial), "value": initial.id})
                field_info["download_autocomplete_info"] = field.autocomplete_choice_kwargs
            else:
                field_info["choices"] = [{"display_name": value, "value": key} for key, value in field.choices.items()]
        elif isinstance(field, serializers.ManyRelatedField) and field.child_relation.queryset is not None:
            if hasattr(field, "autocomplete_choice_kwargs"):
                field_info["choices"] = [
                    {"display_name": str(item), "value": item.id}
                    for item in field.child_relation.queryset.all().order_by("-id")[: self._limit_multiple_choices]
                ]
                # для подгрузки initial в список choices в форме редактирования
                if self._view.kwargs and hasattr(instance := self._view.get_object(), field.field_name):
                    [
                        field_info["choices"].append({"display_name": str(item), "value": item.id})
                        for item in getattr(instance, field.field_name).all()
                        if {"display_name": str(item), "value": item.id} not in field_info["choices"]
                    ]
                field_info["download_autocomplete_info"] = field.autocomplete_choice_kwargs
            else:
                field_info["choices"] = [
                    {"display_name": str(item), "value": item.id} for item in field.child_relation.queryset.all()
                ]
        with suppress(AttributeError):
            if widget := field.parent.Meta.widgets.get(field.source):
                field_info["widget"] = widget
        if hasattr(field, "initial"):
            field_info["initial"] = field.initial
        return field_info

    def determine_actions(self, request, view):
        self._view = view  # возможно эта строчка юзлес
        actions = {}
        for permission in view.get_permissions():
            for method in {"PUT", "POST", "GET"} & set(view.allowed_methods):
                view.request = clone_request(request, method)
                if hasattr(view, "action_map"):
                    for action in set(view.action_map.values()):
                        if not permission.has_action_permission(view.request, view, action):
                            continue
                        if hasattr(view, "serializer_classes") and action in view.serializer_classes:
                            serializer_class = view.serializer_classes[action]
                        else:
                            serializer_class = view.get_serializer_class()
                        if action == "update" and hasattr(view, "get_object"):
                            serializer = self._get_serializer_instance(
                                serializer_class, view, instance=view.get_object()
                            )
                            self._set_update_initial(serializer)
                        else:
                            serializer = self._get_serializer_instance(serializer_class, view)
                        actions[action] = self.get_serializer_info(serializer)
                else:
                    serializer = self._get_serializer_instance(view.get_serializer_class(), view)
                    if hasattr(view, "list"):
                        actions["list"] = self.get_serializer_info(serializer)
        view.request = request
        return actions

    @staticmethod
    def _get_serializer_instance(serializer_class, view, *args, **kwargs):
        """
        Возвращает инстанс сериализатора.

        Если у view аттрибут add_options_action_context установлен в True,
        то добавляет к инстансу контекст из view.get_serializer_context().
        """
        if hasattr(view, "add_options_action_context") and view.add_options_action_context:
            kwargs.setdefault("context", view.get_serializer_context())
        return serializer_class(*args, **kwargs)

    @staticmethod
    def _set_update_initial(serializer):
        for field_name, field in serializer.fields.items():
            field.initial = serializer.data.get(field_name)

    def determine_metadata(self, request, view):
        metadata = super().determine_metadata(request, view)
        metadata["filter_fields"] = get_filter_fields(view)
        metadata["verbose_name"] = get_model_verbose_name(view)
        return metadata