Пользовательские методы Django REST API для общих представлений

#angularjs #json #django #ionic-framework #django-rest-framework

#angularjs #json #django #ionic-framework #django-rest-framework

Вопрос:

Я стажер и работаю над проектом, в котором я разрабатываю DRF API, который должен взаимодействовать с мобильным приложением, написанным моим коллегой с помощью Ionic framework. Мы создаем нового пользователя. Мой метод просмотра следующий:

 class NewUser(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)
  

Когда кто-то хочет создать нового пользователя с помощью POST-запроса, если имя пользователя еще не было введено, API возвращает код состояния 201 и токен в JSON, если имя пользователя уже введено, он возвращает статус 400 и сообщение об ошибке в JSON. МОЙ коллега просит меня изменить сообщение о состоянии на 200, когда он пытается создать имя пользователя с уже существующим именем. Он говорит, что не может использовать ответ об ОШИБКЕ.
Его код выглядит так:

 $http.post(url,{
username:$scope.tel,
password:$scope.passwd
}).success(function(data){
alert(data);
$ionicLoading.hide();
console.log(data);
})
  

Вопрос:
1) Должен ли я настроить свой API для отправки статуса 200 вместо более логичных 400 для ошибки «пользователь уже зарегистрирован»?
Я попытался изменить свой код, но не смог найти метод переопределения в CreateAPIView / ModelSerializer DRF. В итоге я переписал свой класс view в метод:

 @api_view(['POST'])
def newUser(request):
    """
    Saves a new user on the database
    """
    if request.method == 'POST':

        serializer = NewUserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            token, created = Token.objects.get_or_create(user=serializer.instance)
            return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_200_OK)
  

Вопрос:
2) Если я хочу изменить поведение API и ответа, какой метод я должен переопределить
3) Я новичок в Django и до сих пор не знаю, где мы должны использовать общие представления VS. @…. методы

Ответ №1:

200 против 400 в этом случае в основном предпочтение. 400 означает «Неверный запрос». Как правило, это более корректно для неправильно отформатированного запроса, а не для запроса, который не соответствует какому-либо условию.

200 так же уместен, он передает правильную информацию:

Ваш запрос был действительным, но я не создавал новую запись.

Что касается того, как сделать переопределение. Кратчайший путь — переопределить CreateAPIView.create и изменить используемый код ответа. Вам также следует избегать повторения поведения по умолчанию CreateAPIView при вызове super .

 class CreateUserView(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        response = super(CreateUserView, self).create(request, *args, **kwargs)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        response.status = status.HTTP_200_OK
        response.data = {'token': token.key}
        return response
  

Лично я бы также создал my NewUserSerializer , чтобы иметь поле токена и обрабатывать токен, поэтому мне не нужно было выполнять эту работу в View . Он не принадлежит a View .

Комментарии:

1. Спасибо за ваше внимание, но в вашем коде отсутствует та часть, где мы создаем «сериализатор». Таким образом, в строке «token, created = Token.objects.get_or_create (user=serializer.instance)» ошибка глобального имени не определена

2. Справедливо, все, что действительно имеет значение, это изменение статуса ответа. Но еще раз, поскольку у вас здесь неправильное разделение проблем, это заставляет вас дублировать код. Вы должны создавать свое Token творение как часть сериализатора save , чтобы у вас вообще не было всей этой бизнес-логики в представлении и избегать копирования кода вставки из суперкласса.

Ответ №2:

Хуки сохранения и удаления:

Следующие методы предоставляются классами mixin и обеспечивают простое переопределение поведения сохранения или удаления объекта.

perform_create(self, serializer) — вызывается CreateModelMixin при сохранении нового экземпляра объекта. perform_update(self, serializer) — вызывается UpdateModelMixin при сохранении существующего экземпляра объекта. perform_destroy(self, экземпляр) — вызывается DestroyModelMixin при удалении экземпляра объекта.

Эти перехваты особенно полезны для установки атрибутов, которые неявно присутствуют в запросе, но не являются частью данных запроса. Например, вы можете установить атрибут для объекта на основе пользователя запроса или на основе аргумента ключевого слова URL.

https://www.django-rest-framework.org/api-guide/generic-views/#methods

 class CourseOrder(generics.CreateAPIView):
    serializer_class = serializers.OrderCoursesSerializer
    permission_classes = [permissions.AllowAny]

    # hook before creating
    def perform_create(self, serializer):
        # print(serializer['name'].value)

        # save post data
        serializer.save()

        try:
            subject, from_email, to = 'New order', 'zelenchyks@gmail.com', 'zelenchyks@gmail.com'
            text_content = 'New order'
            html_content = '''
               <p>client name: %s </p>
               <p>client phone: %s </p>    
                ''' 
                % (serializer['name'].value, serializer['mobile'].value)

            msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
            msg.attach_alternative(html_content, "text/html")
            msg.send()
        except Warning:
            print('Huston we have a problems with smtp')