Единый вид для нескольких путей через URL kwargs

Я создаю простой API со списком различных растений. В настоящее время он ограничен фильтрацией по одному полю, например, по общему названию, виду и т.д.

Мой urls.py

urlpatterns = [
    path('project/family=<str:family>/', views.SpeciesDetail_family.as_view(), name='family'),
    path('project/species=<str:species>/', views.SpeciesDetail_species.as_view(), name='species')
]

И views.py

class SpeciesDetail_species(generics.ListAPIView):
    serializer_class = SpeciesSerializer

    def get_queryset(self):
        queryset = Species.objects.filter()
        species = self.kwargs['species']
        if species is not None:
            queryset = queryset.filter(species__iexact=species)
        return queryset

class SpeciesDetail_family(generics.ListAPIView):
    serializer_class = SpeciesSerializer

    def get_queryset(self):
        queryset = Species.objects.all()
        family = self.kwargs['family']
        if family is not None:
            queryset = queryset.filter(family__iexact=family)
        return queryset

Как создать единое представление для этих двух путей? Итак, мне просто нужно параметризовать поле поиска (может быть семейство, вид и т.д.) из URL, как /project/species=PlantSpecies/ или /project/family=PlantFamily/.

Если я добавлю kwargs к пути, например path('project/family=<str:family>/', views.SpeciesDetail_family.as_view(), {'lu_field':'family'}, name='family'), как я смогу получить доступ к lu_field в представлениях?

Взгляните на приведенный ниже код и посмотрите, работает ли он так, как вы хотите :

from rest_framework import generics
from .models import Species
from .serializers import SpeciesSerializer

class SpeciesDetail(generics.ListAPIView):
    serializer_class = SpeciesSerializer

    def get_queryset(self):
        queryset = Species.objects.all()
        # Access dynamic lookup field and value from kwargs
        lu_field = self.kwargs.get('lu_field')
        lu_value = self.kwargs.get('lu_value')

        # Filter the queryset dynamically
        if lu_field and lu_value:
            filter_kwargs = {f"{lu_field}__iexact": lu_value}
            queryset = queryset.filter(**filter_kwargs)

        return queryset

и вот urls:

from django.urls import path
from . import views

urlpatterns = [
    path('project/<str:lu_field>=<str:lu_value>/', views.SpeciesDetail.as_view(), name='species_detail'),
]

Вы можете перенаправить оба вида на одно представление и таким образом работать с:

urlpatterns = [
    path(
        'project/family=<str:value>/',
        views.SpeciesDetailView.as_view(),
        kwargs={'search_by': 'family'},
        name='family',
    ),
    path(
        'project/species=<str:value>/',
        views.SpeciesDetailView.as_view(),
        kwargs={'search_by': 'species'},
        name='species',
    ),
]

и позвольте представлению осмотреть self.kwargs['search_by']:

from django.db.models import Q


class SpeciesDetailView(generics.ListAPIView):
    serializer_class = SpeciesSerializer
    queryset = Species.objects.all()

    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(
                Q((f'{self.kwargs["search_by"]}__iexact', self.kwargs['species']))
            )
        )

Я бы настоятельно рекомендовал не разрешать фильтрацию по произвольным полям, поскольку это может привести к раскрытию конфиденциальных данных хакером, выполняющим двоичный поиск по секрету.

Другой вариант, как говорит @Abdul Aziz Barkat в своем комментарии, вы можете использовать атрибут и указать его в .as_view(…) [Django-doc]:

from django.db.models import Q


class SpeciesDetailView(generics.ListAPIView):
    serializer_class = SpeciesSerializer
    queryset = Species.objects.all()

    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(Q((f'{self.search_by}__iexact', self.kwargs['species'])))
        )

и введите это как:

urlpatterns = [
    path(
        'project/family=<str:value>/',
        views.SpeciesDetailView.as_view(search_by='family'),
        name='family',
    ),
    path(
        'project/species=<str:value>/',
        views.SpeciesDetailView.as_view(search_by='species'),
        name='species',
    ),
]
Вернуться на верх