Soufiane Mghanen Soufiane Mghanen - 3 months ago 48
Python Question

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 methond 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 wich part is causing the problem?

Answer

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'),