【问题标题】:Django REST Framework (ModelViewSet), 405 METHOD NOT ALLOWEDDjango REST 框架(ModelViewSet),不允许使用 405 方法
【发布时间】:2016-03-29 01:33:56
【问题描述】:

我正在尝试创建一个 REST API 来注册新用户,我正在使用 Django REST Framework 并使用 AngularJS 调用 API:

当我使用 POST 方法调用 API 时出现此错误:

不允许的方法 (POST):/api/v1/accounts

这是我的代码:

服务器端

views.py

from rest_framework import permissions, viewsets, status, views
from django.contrib.auth import authenticate, login, logout
from authentication.serializers import AccountSerializer
from authentication.permissions import IsAccountOwner
from rest_framework.response import Response
from authentication.models import Account
import json


class AccountViewSet(viewsets.ModelViewSet):
    lookup_field = 'username'
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

    def get_permissions(self):
        if self.request.method in permissions.SAFE_METHODS:
            return (permissions.AllowAny(),)

        if self.request.method == "POST":
            return (permissions.AllowAny(),)

        return (permissions.IsAuthenticated(), IsAccountOwner(),)

    def create(self, request):
        serializer = self.serializer_class(data=request.data)

        if serializer.is_valid():
            Account.objects.create_user(**serializer.validated_data)
            return Response(serializer.validated_data, status=status.HTTP_201_CREATED)

        return Response({
            'status': 'Bad Request',
            'message': 'Account could not be created with received data'
        }, status=status.HTTP_400_BAD_REQUEST)

序列化器.py

from django.contrib.auth import update_session_auth_hash
from rest_framework import serializers
from authentication.models import Account


class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)
    confirm_password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'email', 'username', 'created_at', 'updated_at',
                  'first_name', 'last_name', 'tagline', 'password',
                  'confirm_password',)
        read_only_fields = ('created_at', 'updated_at',)

        def create(validated_data):
            return Account.objects.create(**validated_data)

        def update(self, instance, validated_data):
            instance.username = validated_data.get('username', instance.username)
            instance.tagline = validated_data.get('tagline', instance.tagline)

            instance.save()

            password = validated_data.get('password', None)
            confirm_password = validated_data.get('confirm_password', None)

            if password and confirm_password and password == confirm_password:
                instance.set_password(password)
                instance.save()

            update_session_auth_hash(self.context.get('request'), instance)

            return instance

permissions.py

from rest_framework import permissions


class IsAccountOwner(permissions.BasePermission):
    def has_object_permission(self, request, view, account):
        if request.user:
            return account == request.user
        return False

urls.py

from authentication.views import LoginView, LogoutView
from posts.views import AccountPostsViewSet, PostViewSet
from authentication.views import AccountViewSet
from rest_framework_nested import routers
from django.conf.urls import url, include
from django.contrib import admin
from CVC.views import IndexView

router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)
router.register(r'posts', PostViewSet)
accounts_router = routers.NestedSimpleRouter(
    router, r'accounts', lookup='account'
)

accounts_router.register(r'posts', AccountPostsViewSet)
urlpatterns = [
    url(r'^api/v1/auth/login/$', LoginView.as_view(), name='login'),
    url(r'^api/v1/auth/logout/$', LogoutView.as_view(), name='logout'),
    url('^.*$', IndexView.as_view(), name='index'),
    url(r'^admin/', admin.site.urls),
    url(r'^api/v1/', include(router.urls)),
    url(r'^api/v1/', include(accounts_router.urls)),

对于客户端:

register.controller.js

(function () {
    'use strict';

    angular
        .module('thinkster.authentication.controllers')
        .controller('RegisterController', RegisterController);

    RegisterController.$inject = ['$location', '$scope', 'Authentication'];

    function RegisterController($location, $scope, Authentication) {
        var vm = this;
        activate();

        vm.register = register;
        function register() {
            Authentication.register(vm.email, vm.password, vm.username);
        }

        function activate() {
            if (Authentication.isAuthenticated()) {
                $location.url('/');
            }
        }
    }

})();

authentication.service.js

(function () {
    'use strict';

    angular
        .module('thinkster.authentication.services')
        .factory('Authentication', Authentication);

    Authentication.$inject = ['$cookies', '$http'];

    function Authentication($cookies, $http) {
        var Authentication = {
            getAuthenticatedAccount: getAuthenticatedAccount,
            setAuthenticatedAccount: setAuthenticatedAccount,
            isAuthenticated: isAuthenticated,
            login: login,
            logout: logout,
            register: register,
            unauthenticate: unauthenticate
        };

        return Authentication;

        function register(email, password, username) {
            return $http.post('/api/v1/accounts', {
                email: email,
                password: password,
                username: username
            }).then(registerSuccessFn, registerErrorFn);

            function registerSuccessFn(data, status, headers, config) {
                Authentication.login(email, password);
            }

            function registerErrorFn(data, status, headers, config) {
                console.error('Epic failure!');
            }
        }

        function login(email, password) {
            return $http.post('/api/v1/auth/login/', {
                email: email, password: password
            }).then(loginSuccessFn, loginErrorFn);

            function loginSuccessFn(data, status, headers, config) {
                Authentication.setAuthenticatedAccount(data.data);

                window.location = '/';
            }

            function loginErrorFn(data, status, headers, config) {
                console.error('Epic failure!');
            }
        }

        function logout() {
            return $http.post('/api/v1/auth/logout/')
                .then(logoutSuccessFn, logoutErrorFn);

            function logoutSuccessFn(data, status, headers, config) {
                Authentication.unauthenticate();

                window.location = '/';
            }

            function logoutErrorFn(data, status, headers, config) {
                console.error('Epic failure!');
            }
        }

        function getAuthenticatedAccount() {
            if (!$cookies.authenticatedAccount) {
                return;
            }

            return JSON.parse($cookies.authenticatedAccount);
        }

        function isAuthenticated() {
            return !!$cookies.authenticatedAccount;
        }

        function setAuthenticatedAccount(account) {
            $cookies.authenticatedAccount = JSON.stringify(account);
        }

        function unauthenticate() {
            delete $cookies.authenticatedAccount;
        }
    }
})();

我是 Django 和 AnglarJS 的新手,所以我不知道是哪个部分导致了问题?

【问题讨论】:

  • 我不太确定,但请尝试将基于 AccountViewSet 类的视图中的 create 函数重命名为 post
  • @Jordan 不,我试过了,但还是一样的错误。
  • 在 AccountViewSet 中创建是正确的。但是我有 3 个问题,尽管它没有自定义行为,但为什么要在序列化程序中实现 create?为什么要在视图中实现获取权限?当视图中有新的路由方法时,为什么要使用路由器??
  • 1)序列化器中的create函数,用于反序列化从angularJS中的Authentication服务接收到的数据。关于另外两个问题,我真的无法解释。正如我所说,我是新手,您似乎了解这些活动部分,所以您可以为我提供一些关于在何处实施 get_permissions 以及如何处理视图的指导?

标签: python angularjs django python-3.x django-rest-framework


【解决方案1】:

您的 url 映射中不需要路由器,除非您有除以下之外的自定义操作:

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass

将此添加到您的 views.py

account_list = AccountViewSet.as_view({
    'get': 'list',
    'post': 'create'
})

urls.py 中:

 url(r'^account/$', account_list, name='account-list'),

【讨论】:

  • 对于视图中的 account_list,我收到此错误:“name 'AccountViewSet' is not defined”
  • 您必须在上面分享的AccountViewSet的定义下添加以下代码。
  • 是的,这就是我所做的,我得到了“NameError: name 'AccountViewSet' is not defined”异常
  • NameError Exception 表示他找不到定义,所以检查拼写或缩进。 account_list 应该在类之外定义。
  • 我仔细检查了拼写,并且缩进是正确的(我在类定义内部和外部都尝试了 account_list。我将我的代码推送到 github,以便你可以查看它。github.com/soufiaane/CVC 这是我正在关注的教程,所以你有一个好主意:thinkster.io/django-angularjs-tutorial
【解决方案2】:

问题在于 urls.py。因为url(r'^.*$'... 出现在url(r'^api/v1/ 之前,Django 简单地丢弃后者并将请求路由到IndexView

【讨论】:

    猜你喜欢
    • 2019-05-04
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    相关资源
    最近更新 更多