DRF теряет токен CSRF методом PUT - PullRequest
1 голос
/ 21 мая 2019

У меня есть обычный ModelViewSet в моем проекте, и он отлично работает с GET и POST запросами, но не работает с PUT, возвращая эту ошибку:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

Этомой urls.py :

from django.urls            import path,re_path,include
from django.utils.text      import slugify,camel_case_to_spaces
from PaymentsManagerApp     import views, models
from rest_framework         import routers

APP_NAME = 'PaymentsManagerApp'
router = routers.DefaultRouter()

router.register(r'payments', views.PaymentViewSet)

payments_list = views.PaymentViewSet.as_view({
    'get':'list',
    'post':'create'
})

payment_detail = views.PaymentViewSet.as_view({
'get':'retrieve',
'put':'update',
'patch':'partial_update',
'delete':'destroy'

})

def urlpattern_from_route(route):
    if "regex" in route and route['regex']:
        path_method = re_path
    else:
        path_method = path
    return path_method(route['path'],route['view'].as_view(),name=route['name'] if "name" in route else None)

routes_views = list(map(urlpattern_from_route,routes))
route_services = [

payment_detail = views.PaymentViewSet.as_view({
    'get':'retrieve',
    'put':'update',
    'patch':'partial_update',
    'delete':'destroy'
})

route_services = [
    path('payments/', payments_list, name='rest_payments_list'),
    path('payments/<int:pk>/', payment_detail, name='rest_payment_detail'),
]

urlpatterns = routes_views + route_services

Это мой views.py :

import os
import json
from datetime                           import datetime, timedelta
from django.shortcuts                   import render
from PaymentsManagerApp                 import urls, models, serializers
from FrontEndApp                        import urls as Fronturls
from django.shortcuts                   import render,redirect
from django.contrib.auth.mixins         import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic               import View
from django.contrib.auth.models         import Permission
from GeneralApp.utils                   import get_catalogs
from django.contrib.staticfiles         import finders
from django.utils.text                  import slugify,camel_case_to_spaces
from rest_framework                     import viewsets, permissions
from rest_framework.response            import Response
from django_filters.rest_framework      import DjangoFilterBackend
from rest_framework.response            import Response
from rest_framework.filters             import OrderingFilter, SearchFilter
from django.db.models                   import Q

class PaymentViewSet(viewsets.ModelViewSet):
        exclude_from_schema = True

        permission_classes = (permissions.IsAuthenticated,)
        queryset = models.Payment.objects.all()
        serializer_class = serializers.PaymentSerializer
        filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter,)
        search_fields = ('payment_type', 'creation_user__username', 'provider__name', 'invoice', 'payment_method_type', 'payment_document_number')
        filter_fields = ('id', 'payment_type', 'creation_user', 'provider', 'is_payment_requested', 'is_paid', 'payment_method_type')

Когда я отправляю GET или POST на payments_manager/payments/, он отлично работает.Также, когда я отправляю GET на pyments_manager/payments/<int:pk>/, он работает хорошо.

Проблема в том, что я отправляю PUT на payments_manager/payments/<int:pk>/, потому что получаю следующее:

enter image description here

Я не знаю, почему, но DRF теряет информацию о зарегистрированном пользователе (вместо имени пользователя отображается логин в логине ).

РЕДАКТИРОВАТЬ

Это мой REST_FRAMEWORK в settings.py :

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 20,
    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
}

РЕДАКТИРОВАТЬ Я обнаружил, что ошибка возрастаеттолько если я получаю доступ к конечной точке напрямую из браузера, используя интерфейс базы данных DRF по умолчанию (127.0.0.1:8000/es/payments_manager/payments/1/): enter image description here

Мои запросы PUT отлично работают из моего javascript ajax.

1 Ответ

0 голосов
/ 22 мая 2019

Для того, чтобы использовать сеансовую аутентификацию И сделать POST (немного странно) / PUT / PATCH / DELETE / и т. Д., Вы ДОЛЖНЫ передать заголовок.

см.: https://github.com/django/django/blob/8b3f1c35dd848678225e8634d6880efeeab5e796/django/middleware/csrf.py#L306

Я также создал для вас небольшой тест:

https://gist.github.com/kingbuzzman/20dffbc34d22a899661ac3c065e3f747#file-django_rest_framework_session_vs_token-py-L209

  response = self.client.post('/session-login/', data={'username': 'user', 'password': 'pass'})
  self.assertEqual(302, response.status_code)
  self.assertIn('csrftoken', response.cookies)
  self.assertIn('sessionid', response.cookies)

  # Don't want to go through the trouble of having to get the CSRF from the login form
  self.client.handler.enforce_csrf_checks = True

  csrftoken = self.client.cookies.get('csrftoken').value

  # NOTE: The only reason this works it's because we're passing a header along with the request.
  response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
                               data=json.dumps({'is_paid': 'Y'}), HTTP_X_CSRFTOKEN=csrftoken)
  self.assertEqual(200, response.status_code)
  self.assertEqual('Y', response.json()['is_paid'])

  # NOTE: The reason this DOES NOT works it's because we're NOT passing a header along with the request.
  response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
                               data=json.dumps({'is_paid': 'N'}))
  self.assertEqual(403, response.status_code)

Правка.

Добавил пользователя, залогинился, перешел к localhost/payments/ и добавил запись, затем пошел к записи localhost/payments/1/ и обновил ее (PUT).Все работает.Пожалуйста, добавьте ваши версии django / drf.

enter image description here

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