Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Nested Serialization

00:00 In the previous lesson, I showed you how to use a serializer with a plain Python object. In this lesson, I’m going to show you how to deal with related objects and nested serialization.

00:12 The DRF provides ways to serialize related objects in your ORM. Objects that refer to each other by foreign keys can be identified by an ID. You can also nest serialization by having related objects contained within one another. This time around, I’m going to continue using the vehicles app from the previous lesson.

00:34 You can follow along with the new models, serializers, views, and urls files as shown. If you desire, you can create an admin.py file to go along with the models that are there, or use the one in the supplementary materials. Don’t forget to run makemigrations and migrate as necessary to get your models into the database.

00:54 And of course, you’ll want some data to play with. You can either use the admin or the loaddata command with one of the fixtures in the supplemental materials. Like in the previous lesson, I’m going to use the models/ directory and create a file called vehicles inside of it. The vehicles models file contains two Django definitions, one for a Vehicle that has a single field name, and one for a Part that is related to the Vehicle.

01:22 Each Part has a name and a make and has a foreign key onto the vehicle, so there will be multiple parts in the vehicle.

01:33 Because I’m using the models/ directory, I need to update the __init__ file in order to import the Vehicle and Part so that Python can find these objects.

01:46 And here’s the serializers file. First off, I want to serialize the Vehicle. Except for line 18, everything here is what you’ve seen before. The VehicleSerializer inherits from the ModelSerializer. On line 21, you indicate that this is serializing a Vehicle object. On line 22, you list those fields that you’re interested in. Line 18 is what’s different here.

02:10 I want to nest the Part objects inside of the Vehicle object, and in order to do that, I’m going to add a new field called the PartSerializer. Because vehicles are related to parts through a part set, the field name here is part_set.

02:27 This allows for us to automatically run the query that relates the Vehicle to the Part. Because there could be multiple parts for each vehicle, I’m setting many=True.

02:38 And in this case, I’m going to set read_only=True as well. The reason you do this is so that if you create a new Vehicle, you don’t have to specify the parts that go with it.

02:48 You can just create the Vehicle on its own. You can decide for yourself based on your construction whether you want to create things like parts separately from the vehicles, or do it with a POST containing a nested object.

03:01 The PartSerializer is similar to what you’ve seen before. It’s based on ModelSerializer. The Meta information points it at the Django Part model, and the fields listing lists those fields that are going to be included in the Part.

03:15 Additionally, I’ve added another field here, which is a custom field called SerialNumberField. The SerialNumberField is going to create a custom serial number based on other attributes of the Part.

03:29 Notice that I’m using the source argument for the SerialNumberField. Normally, the DRF looks for the name of the field and uses that to look up the related value inside of the object.

03:43 The Part object has no field named serial_no, so the DRF has no way of looking it up. The argument to source maps this lookup.

03:53 If I wanted to point this at an existing field inside of the Part, I could set the value of source to that field. By setting it to asterisk ("*"), it tells the DRF to just use the field serializer and set the value to whatever the custom field returns. And finally, to tie it all together, here’s the SerialNumberField.

04:15 This is a custom field that inherits from the serializers.Field object and implements the .to_representation() method. When called, the representation takes a value of the thing being serialized, which in this case will be the parent, the Part, and then lines 7 and 8 take information of that Part and return a string, which in this case is based on part of the make field and the id of the Part.

04:44 Each Part that is serialized will include a SerialNumberField. Each of those fields will call .to_representation(), the make and the id of the Part will be combined into a serial number and returned into the serialized object.

05:02 Now to create some views for the Vehicle and Part objects. Like before, I’m creating the vehicles.py file inside of the views/ directory. Line 12 declares the VehicleViewSet. Line 8 declares the PartViewSet. Both of these specify the serializer that they’re associated with, and the only thing that I’ve done a little different here is shown you a shortcut for creating your queryset. Line 15 and 16 you’ve seen before, which is implementing the .get_queryset() method of the VehicleViewSet.

05:36 It returns all vehicles. Alternatively, you can do the same thing by setting the .queryset attribute like I’ve done in line 10. If your queryset is simple, like all objects, the .queryset attribute is the better way to do this.

05:52 If determining your queryset is more complicated, like I showed you when filtering the restricted values of books with indy, then you’ll need to use the .get_queryset() method instead.

06:06 Here’s the vehicles/urls file. I’m creating a new router, registering the VehicleViewSet and the PartViewSet, and then including that on line 12 in my URLs path.

06:19 Here I am in the browser. I’m going to visit the vehicles/ URL.

06:24 This time, I’ve done something that I haven’t done in a while. I’m visiting the vehicles/ base URL. Because the router has registered multiple URLs inside of it, when you hit the base URL, the router shows you all of the possible children.

06:40 This is a convenient way of getting meta information about what your interface provides. Unfortunately, it only works with the router, so the list_tools() method that I created in the previous lesson doesn’t show up here. It’s still there—it just doesn’t show up in this listing.

06:56 But for those things that are created with a router, you can do this automatically. This in and of itself is an argument for always using ViewSets and using your router so that you can take advantage of this meta information.

07:09 I’m going to click on the "parts" listing, and here you have the data that I loaded before, two parts. If you were paying close attention when I showed you the serializers, you would’ve seen that I didn’t include the id field this time. I include url.

07:27 What’s url? url is a special DRF field that returns the URL that specifies the object. This is actually a better alternative to using the id.

07:40 If you have queried this and you have the url available, you can use that url to do patches or puts or deletes afterwards. This means you don’t have to hardcode the URLs for your objects inside of your application. This is definitely a better way to go.

07:58 There are a couple situations here in which you have to do some extra work to get this URL, but in the vanilla mechanism that I’ve just shown you here, it more or less comes for free. I’ll show you the exceptions in the next lesson.

08:11 Because I’m in the web browser, I can just click straight on one of these URLs, and now I can see the detail information for the part that is in the database.

08:23 I’m going to go back out to the Api Root, and now I’m going to click on the vehicles/ URL. Let me just scroll down here a little bit so you can see all of this.

08:34 The payload for the vehicles/ URL contains one vehicle, which is the "U26 Wurrfler", and that vehicle contains a "part_set".

08:44 The "part_set" is an array containing all of the parts that are associated with that vehicle. This is nested serialization.

08:53 This is another good reason use the URLs when you’re referencing these objects. Now you’ve got both the vehicle and the part information available to you in the payload. If you want to change the vehicle, you can use the vehicles/ URL, and if you want to change the part, you can use the parts/ URL.

09:12 That’s how you do nested serialization. In the next lesson, I’m going to dig more into views and ViewSets and how you can specify more actions over and above the standard HTTP methods.

Avatar image for rolandgarceau

rolandgarceau on Feb. 20, 2021

If I did want to create the Part separately and add it to a particular Vehicle, how would I nest a post in here?

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on Feb. 21, 2021

Hi Roland,

You don’t need a nested call to create a Part directly. As there is already a PartViewSet you can post to the URL for Parts. The only trick is the vehicle field which is set to the numeric id of the Vehicle object that you want to associate with the part.

The URL you would be doing a POST to would be:

/vehicles/parts/

And the JSON content would be something like:

{
   "name":"Muffler",
   "make":"DynaMax",
   "vehicle":1
}

I don’t think it is covered in the course, but using nesting you can also do POSTs to create nested objects. This creates a Vehicle and a number of associated Part objects at the same time. You do this by hitting the Vehicles URL and including a number of Part objects inside of a part_set field. This is the opposite of what you asked about, but is a possibility.

Avatar image for SeanmWalker

SeanmWalker on Aug. 25, 2021

I really feel like this course is way too fast paced and doesn’t do a great job of explaining things in a detailed manner. Perhaps this shouldn’t be taken as a detailed course on DRF but perhaps “Hey I know some DRF but need to learn it a little better”. This is not for someone learning DRF that has no prior experience. What a shame.

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on Aug. 26, 2021

Hi Sean,

Pacing is always a challenging thing in courses. I’ll do my best to help if you have a specific question, or you may find more back-and-forth conversation possible in the slack channels. The coding questions channel in particular may be helpful:

realpython-members.slack.com/archives/CGY1X9MKM

Avatar image for coopers

coopers on July 23, 2023

Hi Chris,

I really enjoyed this tutorial. Does nested serialization support pagination? I’m just curious what happens when a vehicle has a very long list of parts.

Thanks

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on July 23, 2023

Hi Coopers,

I’ve never used it myself, but the DRF does support pagination:

www.django-rest-framework.org/api-guide/pagination/

Become a Member to join the conversation.