Vasu Gaur
Vasu Gaur

Reputation: 97

Update/put request in Django REST Framework

I am new to Django REST Framework and trying it for a new project I am working on. So based upon the official tutorial , I am trying to create several get/post/put requests, but with put request I am getting the following error:

Expected view ExampleUpdateView to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field attribute on the view correctly.

Here are my necessary files:

models.py

class ExampleModel(models.Model):
    foo_field = models.CharField(primary_key=True, max_length=15)
    bar_field = models.CharField(max_length=30)
    last_updated_by = models.CharField(max_length=15)
    last_updated_on = models.DateTimeField()

    class Meta:
        managed = True
        db_table = 'example_db'
        unique_together = (('foo_field', 'bar_field'),)

serializers.py

class ExampleSerializer(serializers.ModelSerializer):
    class Meta:
        model = ExampleModel
        fields = ('foo_field', 'bar_field', 'last_updated_by', 'last_updated_on')

urls.py

url(r'^get_example/$', views.ExampleCreateView.as_view()),
url(r'^update_example/(?P<pk>\d+)/$',views.ExampleUpdateView.as_view()),

views.py

class ExampleCreateView(generics.CreateAPIView):
    serializer_class = ExampleSerializer
    queryset = ExampleModel.objects.all()

class ExampleUpdateView(generics.UpdateAPIView):
    queryset = ExampleModel.objects.all()
    serializer_class = ExampleSerializer

I think the problem I might have is that I have a composite key. I tried other generic views (CreateAPIView & ListAPIView) and they work absolutely fine. Do I need to update the def_update method? Do I need to change anything in serializers.py?

This is an existing JSON object that I got from my GET request and was trying to update:

{
    "foo_field": "john",
    "bar_field": "doe",
    "last_updated_by": "batman",
    "last_updated_on": "2017-02-09"
}

I looked at the following duplicates but none of these solution seems to work for me:

Django Rest Framework: Unclear error message

Django REST Updateview with PUT POST

How do I use an UpdateView to update a Django Model?

Upvotes: 0

Views: 20139

Answers (2)

cezar
cezar

Reputation: 12012

First I'll refactor your code and present the code examples. Afterwards I'll explain the changes.

models.py

class ExampleModel(models.Model):
    foo_field = models.CharField(primary_key=True, max_length=15)
    # rest ...

You have set foo_field as primary key. It is a bad practice to use strings (VARCHAR) as primary key. It is highly recommended to use surrogate keys. This will be accomplished automatically by Django. It adds a field id as primary key.

The serializers.py will now work as it is. No changes are needed. Also the views.py should work without changes. However I'll cover the option with foo_field as primary key.

views.py

class ExampleUpdateView(generics.UpdateAPIView):
    # rest ...
    lookup_field = `foo_field`

You could also leave this out, but it is important to make some changes in your urls.py.

urls.py

url(r'^examples/$', views.ExampleCreateView.as_view()),
url(r'^examples/(?P<pk>\d+)/$',views.ExampleUpdateView.as_view()),

In order for a web services to be ReSTful, they have to comply with the REST principles. One of the most important rules is that we shouldn't use a verb in the URL for describing the action. Another convention is that the resource name is used in plural to describe the collection and an identifier is used to access single resource.

Your resource is called example, therefore we use examples as collection name. Verbs like get or update have no place in the URL. The HTTP method is responsible for the proper action.

If you'd like to use foo_field as primary key, then you have to change the url for the update view:

url(r'^examples/(?P<pk>[\w-]+)/$',views.ExampleUpdateView.as_view()),

This is because your foo_field is a CharField (or string), so the arguments passed to it should match any alphanumeric character (\w) and the hyphen (-).
You can use foo_field as named group in your url regex instead of pk if you wish. Pay attention that you adapt that in your view, setting the correct values for lookup_field and lookup_url_kwarg.

Again I'll strongly recommend to use surrogate keys and leave Django to accomplish this task.

Now you can understand why ListAPIView and CreateAPIView worked. They don't require an argument passed to the url and were properly called.
Your UpdateAPIView couldn't work because the matching url was accepting only digits and didn't match your primary key foo_field.

Upvotes: 5

Ishita Sharma
Ishita Sharma

Reputation: 421

Use update() wiht your UpdateAPIView.

class ExampleUpdateView(generics.UpdateAPIView):
        queryset = ExampleModel.objects.all()
        serializer_class = ExampleSerializer

        def update(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.foo_field = request.data.get("foo_field")
            instance.bar_field = request.data.get("bar_field")
            instance.last_updated_by = request.data.get("last_updated_by")
            instance.last_updated_on = request.data.get("last_updated_on")
            instance.save()
            serializer = self.get_serializer(instance)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
            return Response(serializer.data)

Upvotes: 4

Related Questions