Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework (ModelViewSet), 405 METHOD NOT ALLOWED

I'm trying to create a REST API to register new users, I'm using Django REST Framework and calling the API using AngularJS:

when I call the API using POST method I'm getting this error:

Method Not Allowed (POST): /api/v1/accounts

Here is my code:

for the Server side

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)

serialisers.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)),

and for the client side:

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;
        }
    }
})();

I'm new to both Django & AnglarJS, so I don't know which part is causing the problem?

like image 516
Soufiaane Avatar asked Dec 22 '15 17:12

Soufiaane


People also ask

What is APIView in Django?

REST framework provides an APIView class, which subclasses Django's View class. APIView classes are different from regular View classes in the following ways: Requests passed to the handler methods will be REST framework's Request instances, not Django's HttpRequest instances.


2 Answers

You don't need router in you url mapping, unless you have a customized action other than the following:

    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

add this to your views.py:

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

in urls.py:

 url(r'^account/$', account_list, name='account-list'),
like image 111
Dhia Avatar answered Sep 20 '22 11:09

Dhia


The problem is with urls.py. Because url(r'^.*$'... comes before url(r'^api/v1/, Django simply discards the latter and routes the request to the IndexView.

like image 38
Irina Velikopolskaya Avatar answered Sep 22 '22 11:09

Irina Velikopolskaya