Categories
Python

Django REST Framework: ModelSerializer and Generic Views

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post on Serializers, we learned how to use Serializers with APIViews. In this post we will discuss how ModelSerializer and the Generic views can take things even further.

Model + Serializer = ModelSerializer

That one line equation is probably enough to explain the concepts of ModelSerializers. In our example, we had to define similar fields on both Serializer and the Model class. We had to write codes for the same fields twice. That is 2x the efforts. ModelSerializers can help solve that problem. A ModelSerializer might remind us of ModelForm. The idea is the same. We extend ModelSerializer and pass it the model. The serializer inspects the model and knows what fields to use and what their types are.

Let’s refactor our old serializer to be a ModelSerializer:

That’s all we need – our new SubscriberSerializer now infers the fields from the model (Subscriber) we passed to it. We can however choose which fields should be used while serializing / deserializing. In this example we pass the special value “__all__” which means we want to use all fields. But in many cases we would want to selectively use some fields. For example, for Django’s default User model, we don’t want to leak the password data to public, so we will exclude the password field on our User model serializer.

If we try out our API, we would notice everything is working just like before. Except our serializer class is now shorter and more concise. Awesome, right? Let’s move on to our next topic – generic views.

Generic Views

If you have decent amount of experience with Django, you probably have already come across and used the built in generic views. They provide useful functionality around common database operations. For example, we can use the generic list view and provide it with a queryset and the template, it will do the rest for us.

In the same way, we can pass a queryset and serializer to a ListAPIView and it will create the list view for us. Same way we can use CreateAPIView to implement the create view. What’s even better, since we’re using class based views, we can use both of them together, cool, no? Let’s refactor old code to use these two classes.

Please note how we no longer need to provide the get and post methods but the API still works. The generic views just know how to deal with those.

There is actually a ListCreateAPIView which is the combination of these two as you can probably understand from the class name. We can just use that one.

As you can see this generic view is quite helpful for generating “collection” resources (/api/subscriber) easily. There’s also RetrieveUpdateDestroyAPIView which we can use to generate “element” resources. As you can guess from the name, they provide get, put, patch and delete handler for single items (/api/subscriber/12).

Generic views are very handy for quickly generating resources from our models (querysets actually). This allows rapid API development.

What’s Next?

We have seen how we can very quickly create working REST APIs using model serializers and generic views. Things will be even more amazing when we learn about ViewSets, specially ModelViewSets. You will have to wait for our next post for those 🙂

In case you didn’t notice, we have a mailing list where you can subscribe to get latest updates. We don’t spam, only contents we post. Also if you enjoyed reading the post and found it informative, shouldn’t you share it with your friends? 😀

Categories
Python

Django REST Framework: Serializers

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last blog post, Getting started with Django REST Framework, we saw how we could use the APIView and accept inputs from users using request.data. In our example, we dealt with string, so it was pretty straightforward. But consider the case of age or account_balance – one has to be integer, the other has to be float / decimal. How do we properly validate the incoming data?

We can manually check every input field and send an error if the field type doesn’t match. But soon we’re going to have a problem at our hand – when the number of inputs will grow, we can’t just keep doing this kind of manual validation. In Django, we would probably use Django Forms for validation. Does DRF provide us with something similar? Yes, it does. The solution to our problem is Serializers.

What can a Serializer do for us?

Have you ever tried JSON serializing a Django model? Or a queryset? You can’t directly because they are not JSON serializable. So what do we do instead? We convert them to Python’s native data structures which could be serialized into JSON. We can serialize querysets into lists and model instances to dictionaries. But doing that by hand is cumbersome.

On the other hand, we saw how we can get incoming data from request.data – we get this data as key value pairs. We can’t just store them in database directly – we have to transform them into Django’s data structures like models and querysets. Doing that by hand is also cumbersome.

Serializers can help us with that. It can serialize complex types into Python natives types and then again deserialize native types into those complex types. Besides that, it also does basic validation based on the serializer field types. If a field is defined as an integer field, it will raise an error if we pass a string to that field. If we need more advanced validation rules, we can plug in the built in Validators or even write our own. Let’s see code examples to understand the use case better.

Defining a Serializer

Create a file named serializers.py inside the api app directory. Put the following codes into it.

We’re creating a HelloWorldSerializer which extends serializers.Serializer. We’re defining two fields on this serializer –

  • name is a CharField so it accepts string. It has a max_length of 6.
  • age is an optional integer field. The value must be at least 10 if provided. If not provided, default value will be 10.

With this serializer setup, let’s modify our view to use it.

We pass the request.data as the data parameter to HelloWorldSerializer so it can read all the request data and parse them. Then we check if the serializer is valid. If you have used Django Forms, this will feel very similar. If the serializer is valid, that means we have a valid set of data available. So we can take the value of name and age and show a pretty message. On the other hand, if the serializer is not valid, we can pass the serializer.errors back to the client, which will contain elaborate error messages.

Let’s try out the API to see what happens. Let’s first send an empty request:

The errors say the name field is required. Of course it is! Let’s pass the name.

We just passed the name but didn’t pass the age. Since it is not required and has a default value set, we get the default value. But what if we set a low value?

So we passed 8 and it’s not happy about that. Please note we passed the 8 as a string but DRF doesn’t mind as long as it can convert it to an integer successfully. What if we pass a value that is no number?

That works too! Cool, okay then let’s give it a rest and pass a valid value.

 Serializer with Model

How does Serializers help us in working with models? To understand that, let’s first create one model.

Creating the Subscriber Model

Open api/models.py and add the Subscriber model like this:

Now create and run the migration.

That should setup the table for our new model.

Update The Serializer

We added an email field to our model, also the max length for name is now 50 chars. Let’s update our serializer to match these constraints. Also rename it as SubscriberSerializer.

Update The View

And now let’s refactor our view.

The code is very simple and straightforward. If the serializer validation succeeds, we create a new subscriber out of the validated data.

Update URLConf

Let’s update the urls.py to update our url end point.

Now let’s try it out. We will post the following JSON using curl or postman:

And we will get back the following response:

With the serializer, we needed so much less codes. And we did it in a very clean way.

List All Subscribers

According to the REST Best Practices, the GET call to a resource route (/api/subscriber) should return a list of all the items (subscribers). So let’s refactor the get method to return the subscribers list.

We are fetching all subscribers and then passing the queryset to the serializer constructor. Since we’re passing a query set (not just a single model instance rather a list of model instances), we need to set  many=True . Also note, we don’t need to call is_valid – the data is coming from database, they’re already valid. In fact, we can’t call is_valid unless we’re passing some value to the data parameter (SubscriberSerializer(data=request.data)). When we pass queryset or model, the data is automatically available as serializer.data.

What’s Next?

We have so far learned how to use APIView and Serializer to build beautiful APIs with clean code. But we still have some duplication of efforts, for example when we had to define fields on both the model and the serializer. Also, we have to implement both collection and element resources. So that’s like 5 method implementation. Wouldn’t it be great if, by some mechanism we could make things simpler, shorter and cleaner?

We’ll see, in our next blog post 🙂 Please subscribe to our mailing list for email updates. Also if you liked the content, please don’t forget to share with your friends!

 

Categories
Python

Django REST Framework: Getting Started

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post about Building APIs in Django, we explained why using Django REST Framework would be a good idea. In this post, we will start writing our APIs using this awesome framework. DRF itself works on top of Django and provides many useful functionality that can help with rapid API development.

Installing Django REST Framework

We have to install DRF first. We can install it using pip as usual.

Once the installation succeeds, add rest_framework to the INSTALLED_APPS list in settings.py.

Now we are ready to start building our awesome APIs!

Using APIView

APIView class is quite similar to Django’s View class except it is more REST-y! The APIView class can be considered as quite similar to the Flask Resource class from our Flask Tutorial. An APIView has methods for the HTTP verbs. We can implement our own methods to handle those requests the way we want.

Let’s modify the function we wrote in our first post to use APIView instead.

We have done two things –

  • Our HelloWorldView extends APIView and overrides the get method. So now DRF knows how to handle a GET request to the API.
  • We return an instance of the Response class. DRF will do the content negotiation for us and it will render the response in the correct format. We don’t have to worry about rendering JSON / XML any more.

Since we are now using a class based view, let’s update the urlconf and make the following change:

That’s all the change that is necessary – we import the class based view and call the as_view method on it to return a view that Django can deal with. Under the hood, the as_view class method works as an entry point for the request. The class inspects the request and properly dispatches to the get, post, put etc methods to process the request. It then takes the result and sends back like a normal function based view would do. In short, the as_view method kind of works as a bridge between the class based view and the function based views commonly used with Django.

The Web Browsable API

If you visit http://localhost:8000/api/hello you will now see a nice html view with our json response displayed along with other useful information (response headers). This html view is an excellent feature of DRF – it’s called the web browsable API. The APIs we create, DRF automagically generates a web view for us from where we can interact with our API, testing / debugging things. No need for swagger or any other external clients for testing. Awesome, no?

Function Based APIView

We can also use a function based form of APIView where we write a function and wrap it using the api_view decorator. An example would look like this:

And in the urls.py, the entry will look like this:

I mostly use function based views when things are really simple and I have to handle just one type of request (say, POST or GET). But in case I have to handle multiple type of requests, I will then have to check request.method to determine the type and handle accordingly. I find the class based view cleaner and well organized than writing a bunch of if else blocks.

You can read more about the function based APIView in the docs.

Accepting Input

We have seen how to write a simple end point to say “hello world!” – that is great. But now we must learn how we can handle inputs from our user. For this demonstration, on our /api/hello endpoint, we would accept a name in a POST request. If the name is passed, we will show a customized greeting. Let’s get to work!

We have added a post method that should handle the POST requests. Instead of request.POST, we would use request.data which works across POST, PUT, PATCH  – all other methods too. If the name is not passed we send error message. Otherwise we send a hello world message.

With that code written, let’s try it out –

Aha, things worked as expected! Cool! What if we don’t pass the name?

It works exactly like we wanted it to! Fantastic!

What’s next?

In this post, we saw how the APIView works and how we can accept inputs and send responses for different http verbs. In the next post, we will discuss about serializers and how they can be useful.

If you would like to get notified when we post new content, please subscribe to our mailing list. We will email you when there’s something new here 🙂