Django Rest Framework viewset - фильтр фильтрации ForeignKey на основе имени пользователя - PullRequest
0 голосов
/ 23 января 2019

У меня есть проект django, и я интегрировал Django Rest Framework в проект для серверной части. У меня есть модель профиля. В модели профиля у меня есть пользовательский ключ пользователя, который имеет поле имени пользователя. Имя пользователя - это то, что я сейчас использую для фильтрации профилей.

У меня все работало отлично, когда ListAPIView и RetrieveAPIView были разделены.

class ProfileListView(ListAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

class ProfileDetailView(RetrieveAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

    def get_object(self):
        return self.queryset.get(user__username=self.kwargs.get('username')) 

Другое дело, что, когда представления были разделены, я установил параметр имени пользователя в URL, который был передан в представление. Я не уверен, как интегрировать это в viewsets.

path('users/', UserListView.as_view()),
path('users/<username>', UserDetailView.as_view()),

это то, что у меня сейчас

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'profiles', ProfileViewSet, basename='profile')
urlpatterns = router.urls

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

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

DoesNotExist at /api/users/profiles/omarjandali/
Profile matching query does not exist.
Request Method: GET
Request URL:    http://localhost:8000/api/users/profiles/omarjandali/
Django Version: 2.1.5
Exception Type: DoesNotExist
Exception Value:    
Profile matching query does not exist.
Exception Location: /Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/site-packages/django/db/models/query.py in get, line 399
Python Executable:  /Users/omarjandali/anaconda3/envs/splittapp/bin/python
Python Version: 3.7.2
Python Path:    
['/Users/omarjandali/Documents/splittapp/backend/src',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python37.zip',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/lib-dynload',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/site-packages']
Server time:    Wed, 23 Jan 2019 03:21:44 +0000

1 Ответ

0 голосов
/ 23 января 2019

Дело в том, что у наборов данных есть то, что называется lookup_field, унаследованное от GenericAPIView.По сути, это поле, используемое для извлечения одного объекта в конечной точке детализации набора.По умолчанию это обычно поле id, поэтому он пытается найти профиль с id=omarjandali, но, конечно, он не существует, так как это имя пользователя.

Таким образом, если вы хотите использовать имя пользователя вместо идентификаторов для поиска, вы должны указать lookup_field в качестве имени пользователя.Поэтому у вас должно быть что-то вроде этого:

class MyViewSet(vieswts.ModelViewSet):
    ...
    lookup_field = 'user__username'
    ...

Имейте в виду, что это означает, что вы больше не можете получать профили по их id.

Чтобы разрешить более сложные поиски с использованием более чем одного поля поиска, вы должны переопределить метод get_object() и реализовать там логику.

Для этого вы можете использовать этот миксин, который я построил ииспользовать в моих проектах.

class AlternateLookupFieldsMixin(object):
"""
Looks up objects for detail endpoints using alternate
lookup fields assigned in `alternate_lookup_fields` apart
from the default lookup_field. Only unique fields should be used
else Http404 is raised if multiple objects are found
"""

alternate_lookup_fields = []

def get_object(self):
    try:
        return super().get_object()
    except Http404:
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        queryset = self.filter_queryset(self.get_queryset())
        obj = None
        for lookup_field in self.alternate_lookup_fields:
            filter_kwargs = {lookup_field: self.kwargs[lookup_url_kwarg]}
            try:
                obj = get_object_or_404(queryset, **filter_kwargs)
            except Http404:
                pass
        if obj:
            self.check_object_permissions(self.request, obj)
            return obj
        raise Http404

Как его использовать:

Добавьте миксин к своим наборам и укажите дополнительные поля, которые будут использоваться для поиска в alternate_lookup_fields список.Это означает, что вы можете (скорее всего, должны) разрешить идентификацию исходного lookup_field и добавить дополнительные поля поиска в список.

Имейте в виду, что вы должны использовать только уникальные поля для поиска, иначе вывозникнут проблемы, при которых будет найдено несколько объектов.

Итак, в вашем случае использование миксина будет выглядеть так:

class MyViewSet(AlternateLookupFieldsMixin, vieswts.ModelViewSet):
    ...
    alternate_lookup_fields = ['user__username']
    ...
...