MarkoBox
MarkoBox

Reputation: 95

PUT method not allowed

I'm trying to make editable data tables to work with Django REST Framework. I'm trying to use PUT method for table field changes, but I'm getting:

Method Not Allowed (PUT): /api/zaposleni/2/

Method Not Allowed: /api/zaposleni/2/

I have checked similar questions on stackoverflow. I'm returning the key in URL and in the request body, also PUT method is decorated which should not trigger the error I'm getting. Adding breakpoints seem to show that code execution does not even reach my view.

View:

class ZaposleniDetail(viewsets.ViewSet):
    @action(detail=True, methods=['put'])
    def put(self, request, pk):
        
        try:
            zaposleni = Zaposleni.objects.get(pk=pk)
        except Zaposleni.DoesNotExist:
            return HttpResponse(status=404)

        if request.method == 'PUT':
            # this is when you make changes with datatables editor. I think there was a reason I used PUT instead of POST
            # but I cant remember why that was right now.
            # change pk to an int
            pk = int(pk)

            # parse request querydict into dict
            # I could not find a better way to do this.
            data = parser.parse(request.body)

            # extract out the nested dict needed for drf post
            # datatables always send it nested in 'data' but you can change that if you want to
            parsed_data = data['data'][pk]

            for key, val in parsed_data.items():
                # this is the ugliest part of the function
                # This looks at every value in the dictionary and if it is an empty string then it skips it.
                # An empty string means there wasn't a change
                # If it has data if will go through the keys until it gets a match and then update that in the database
                # The problem is I don't want it to update everything because if there was no change it will send a blank string
                # and then it could overwrite an existing value in the database with a blank string.
                if key in parsed_data:
                    if val == '':
                        continue
                    else:
                        if key == 'ima_prezime':
                            Zaposleni.objects.filter(pk=pk).update(ima_prezime=val)
                        if key == 'datum':
                            Zaposleni.objects.filter(pk=pk).update(
                                datum=val)
                        if key == 'boolean':
                            Zaposleni.objects.filter(pk=pk).update(boolean=val)

            # After making a change in the database you will need to send the data back to datatables
            # If it doesn't match what datatables sends then it will not refresh the datatable
            # this code formats everything to match what datatables send in the PUT request
            parsed_data['id'] = str(pk)

            serializer = ZaposleniSerializer(
                Zaposleni.objects.filter(pk=pk), many=True)
            data = serializer.data

            # This will nest it back into the 'data' dict
            data_dict = {'data': data}
            json_data = json.dumps(data_dict)

            return HttpResponse(json_data)

URL:

urlpatterns = [
    url(r'^api/zaposleni/$', views.ZaposleniList.as_view(), name='zaposleni-list'),
    url(r'^', views.TemplateView.as_view(template_name="zaposleni.html")),
    # loads the template
    url(r'^api/zaposleni/(?P<pk>[0-9]+)/$', views.ZaposleniDetail, name='zaposleni-detail'),
    # view to get and post changed to the database with datatables editor
]

jquery:

$(document).ready(function () {
    editor = new $.fn.dataTable.Editor({
        ajax: {
            url: 'api/zaposleni/_id_/',
            type: 'PUT',
            headers: {'X-CSRFToken': '{{ csrf_token }}'},
        },
        "table": "#example",
        "idSrc": 'id',
        "fields": [    
            {
                "label": "Ime Prezime",
                "name": "ima_prezime",
            },
            {
                "label": "Datum",
                "name": "datum",
                "type": "date",
            },
            {
                "label": "Boolean",
                "name": "boolean",
                "type": "select",
                options: [
                    {label: "", value: ""},
                    {label: "True", value: "True"},
                    {label: "False", value: "False"}
                ]
            },
        ]
    });
});

Upvotes: 0

Views: 1028

Answers (1)

marxin
marxin

Reputation: 3922

This is because the query is hitting a different view (TemplateView), which obviously doesn't accept PUT method. Django is trying to match the URL from top to bottom, and your second url regex:

url(r'^', views.TemplateView.as_view(template_name="zaposleni.html"))

is matching /api/zaposleni/2/. This is because there is no $ and the end of the regex.

It should be:

url(r'^$', views.TemplateView.as_view(template_name="zaposleni.html"))

Upvotes: 1

Related Questions