Search code examples
djangodjango-rest-frameworkdjango-viewsdjango-serializer

Odd behaviour on REST: Update/patch View doesn't allow get method and returns 405 when called via requests


Similar questions have been asked before, but I can't seem to fix my problem with the answers.

I am trying to partially update an object and I overwrote the partial_update method in REST's generic UpdateAPIView but when I call this view via requests I get a 405:get method not allowed-response.

What is extremely odd is that my code was working for about 2 weeks and now all of the sudden I get this error. I'm banging my head against a wall and can't seem to figure this out.

In my view I am doing this:

class BuildingUpdateAPI(UpdateAPIView):
    serializer_class = BuildingSerializer

    def partial_update(self, request, *args, **kwargs):
        """Patches building specified in URL with data in request."""

        network = Network.objects.get(name=self.kwargs["name_network"])
        building = network.buildings.get(name=self.kwargs["name_building"])

        serializer = BuildingSerializer(building, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(status=200, data=serializer.data)
        msg = (
            "Error message"
        )
        return JsonResponse(status=400, data=msg, safe=False)

My serializer is simple, it only defines the fields (but I can provide it if it helps).

In other answers it is said that REST looks for a get-method and can't find it. But I am calling this not via get, but via patch:

ep = "http://localhost:8000/path/to/update/"
data = {
   "update_field": "123"
}
r = requests.request("patch", url=ep, data=data, headers=get_headers())

Other answers say I am calling a wrong URL, but I am quite sure that it is the correct URL. And again, this was working before and now it doesn't . Or can I not call Network.objects.get within an update method? But that how could I do anything then...

And to make this thing even more ridiculous: I can update just fine using with REST-interface. But sending a patch-request via requests gives me that error (But I need to do that with requests)

I'm really out of options, any help is very much appreciated. Thanks in advance. If I can provide any more code or info just lmk.

Thanks.

Update: Also trying this via Postman gives the same error, so it's not a problem with requests I guess.

urls.py:

urlpatterns = [
    path(
        "api/v1/networks/<int:pk>/",
        NetworkDetailAPIById.as_view(),
        name="networks-detail-api-id",
    ),
    path(
        "api/v1/networks/<str:name>/",
        NetworkDetailAPIByName.as_view(),
        name="networks-detail-api-name",
    ),
    path(
        "api/v1/networks/update/<int:pk>/",
        NetworkUpdateAPI.as_view(),
        name="networks-update-api",
    ),
    path(
        "api/v1/networks/buildings/new/",
        NetworkBuildingCreateAPI.as_view(),
        name="networks-buildings-create-api",
    ),
    path(
        "api/v1/networks/project/<str:project_name>",
        NetworkProjectListAPI.as_view(),
        name="networks-project-list-api",
    ),
    path(
        "api/v1/buildings/<int:id>/",
        BuildingRetrieveAPI.as_view(),
        name="buildings-detail-api",
    ),
    path(
        "api/v1/buildings/delete/<int:id>",
        BuildingDeleteAPI.as_view(),
        name="buildings-delete-api",
    ),
    path(
        "api/v1/buildings/update/<str:name_network>/<str:name_building>",
        BuildingUpdateAPI.as_view(),
        name="buildings-update-api",
    ),
    path(
        "api/v1/network/buildings/<str:name_network>/",
        BuildingByNetworkListAPI.as_view(),
        name="network-buildings-list-api",
    ),
    path(
        "api/v1/time-series/",
        TimeSeriesAPI.as_view(),
        name="time-series-list-create-api",
    ),
    path(
        "api/v1/time-series/network/building/type-data/<str:name_network>/<str:name_building>/<str:type_data>/",
        TimeSeriesBuildingInNetworkAPI.as_view(),
        name="time-series-building-data-type-detail-api",
    ),
    path(
        "api/v1/time-series/project/type-data/step/<str:project>/"
        "<str:type_data>/<str:time_step>/",
        TimeStampBuildingsAPI.as_view(),
        name="timestamp-detail-api",
    ),

    path("api/v1/bldg/<str:name_network>/<str:name_building>/", SampleCreate.as_view(), name="sample")
]

Error when adding get:


500
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>AssertionError
          at /de/api/v1/bldg/TestDevNetworkBuilding4/Demand_1/</title>
  <style type="text/css">
    html * { padding:0; margin:0; }


..... plus a bunch of html 

Solution

  • Your path is missing the port component:

    ep = "http:localhost:/path/to/update/"

    should be something like:

    ep = "http:localhost:5000/path/to/update/"

    UPDATE:

    Try adding a patch method to the class:

    def patch(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.partial_update(request, *args, **kwargs)
    

    Then, change to :

    r = requests.request("PATCH", url=ep, data=data, headers=get_headers())
    

    or try:

    r = requests.patch(url=ep, data=data, headers=get_headers())
    

    UPDATE 2:

    After troubleshooting:

    That's an assertion error from /de/api/v1/bldg/TestDevNetworkBuilding4/Demand_1 which matches your SampleCreate.as_view() URL pattern. The url is hitting the path("api/v1/bldg/<str:name_network>/<str:name_building>/" match.

    Possible causes: you are confusing bldg and buildings in your url, or adding another url parameter, like language de, en, etc.