Headless Wagtail CMS: Serializing ForeignKeys on Wagtail Pages

This video features Kalob Taulien at Wagtail CMS 2023 .

Headless Wagtail CMS: Serializing ForeignKeys on Wagtail Pages
0:10:02
Published November 29, 2023
1,902 views

Tutorial: https://learnwagtail.com/tutorials/headless-cms-serializing-foreign-keys/
Wagtail for Beginners Course: https://learnwagtail.com/wagtail-for-beginners/

In this video we'll change the default ForeignKey information that's provided by Wagtails v2 API to something a little more useful. The idea is to make less API requests from your headless application, and to provide more useful data on the initial request.

ForeignKey's a wildly helpful feature in Django and Wagtail. But in our Wagtail v2 API we're provided with a couple meta fields, the page id, and the page title. But what if we want more (or less)? That's when custom serialization comes in.

For more free tutorials: https://learnwagtail.com/tutorials/
Don't forget to subscribe to this channel and follow me on Twitter at https://twitter.com/kalobtaulien

Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/3465f281ed9526c140308fe7d6c0621aac24b24d

#Wagtail #Django #Python

Summary

A Wagtail page API normally serializes a ForeignKey to another page as limited metadata, requiring a second API request to retrieve the linked page. The speaker shows how to attach a custom Django REST Framework field serializer to the API field and override `to_representation()` so the response includes selected values such as the linked page’s ID, title, publication date, owner username, slug, and URL. The serializer can also contain arbitrary logic and return strings, lists, dictionaries, or other useful API data.

Key takeaways

  • A Wagtail ForeignKey to a page can be serialized into a custom dictionary rather than the default linked-page metadata.
  • Set the API field’s `serializer` option to a custom Django REST Framework `Field` class.
  • Override `to_representation(self, value)` and read attributes from the linked page to construct the API response.
  • Related objects such as the page owner may need their own serialization or a simple attribute such as `username`.
  • The custom method can perform additional logic and return any useful serializable string, list, or dictionary.

Summarised automatically from the transcript.

Transcript

1,922 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

Hello and welcome to another lesson of Learn Wagtail. In this video, we are going to talk about a custom serializer and how we can turn a foreign key into something that's a little more useful than just an ID. Now as an example, I'm going to boot up this server just on my local host here, and I'm going to show you exactly what I mean in the API, and then we're going to fix it. And hey, if you are watching this on YouTube, don't forget you can subscribe, click that little notification icon, and you'll get updates whenever there's a new Wagtail video. So I'm just going to get into my server here and uh python manage. py run server 0. 0. 0. 0 port 8000. So nothing exciting there. And let's open this up in our browser. And let's just go straight to our API. So API slash v2

0:46

slash pages and let's take a look at some pages. Now I've cleared off my database uh since the last video so this is fairly fresh data in here. And what is a good example? I just saw this not too long ago. I think it's actually on the blog page or possibly the homepage. Let's look at the homepage first. So let's go to page ID three that matches here. We're just going to go to the detail view here. And we have uh yeah, banner image, banner CTA. So this is a foreign key, and I'll show you the code in just a moment, but this is a foreign key. to page ID number four. And we can go and fetch this data. We can say, oh that's page ID number four. So we could go get a we can perform a GET request against our API.

1:32

And use this URL and we can get all the fields and stuff that we want from there. And that's fine. But that is a secondary API request, which means more processing for your server, which means more processing on the browser, which means a longer wait time for your user, and all that stuff. But what if you didn't want just the ID? What if you wanted the ID, uh the URL, the title, and you know, maybe something else. And you just wanted it just directly on your home page. Let's go and explore some code now. So that was on our homepage. So let's open up homemodels. py and we've got our banner CTA in here. And you can see that I'm in homemodels. py. I'm in the home page class. It's a Wagtail page. And this is banner CTA is equal to models. foreinkey, and it's a Wagtail Core page.

2:20

Now, just to really clarify what this is doing, let's go into the admin here and let's edit. The home page. And in our banner settings, this is a custom tab we made with a tabbed interface in another video. But in our banner settings here, we've set the banner CTA to be the blog page. And that's where it's getting number four from is that blog page. And you can actually see that it's the page is a blog listing page, and it's going to uh the detail page of ID number four here. And I'll make that API page just a little bigger. Now that's great, and we can work with that, but what if we wanted to perform a you know a smarter API request? Well let's go ahead and work with this so it doesn't just return the ID or just the title, maybe it returns a bunch of other stuff.

3:06

So first things first we have our banner CTA in here, and let's go down to our API field and we're going to use a custom serializer. And we do that with a serializer is equal to and then just the name of the serializer. And you can see that I'm using VS Code here, and it actually says the API field takes a name and then a serializer and by default there is no extra serializer. So let's go ahead and add a custom serializer in here So let's call this banner CTA serializer and it's going to be a class so it takes parentheses. Now I'm going to do do do do do come up here and I'm going to create a new serializer. Class, banner CTA serializer, and this is going to be a field. Now I just need to make sure that this is actually imported.

3:52

from rest framework. And I don't think it is. Let's do rest framework. Okay. And just as an FYI, uh a little late introduction here. If you don't have the Wagtail V2 API installed and up and running, you're going to want to check out the video on that so that you have this up and running to begin with. It doesn't come enabled with Wagtail by default, but it's really easy to enable it. And I have a a couple videos on that as well. So let's go in here and let's add our REST framework fields streams. And let's just throw it in here. So from Rest Framework, and we're now getting into Django RestFramework, dot fields, import, just a regular field. And I'm just gonna go back to where this is and all we did was import that field.

4:39

So now we have this special function in here, the special method called To representation rep if I can spell that right, to representation, there we go. Is that already in here by any chance? Nope, it's not. And it's going to take self because it's object-oriented programming in Python, so it always takes self. And it's going to take a value. And in here all we have to do is return something custom. So let's do this. Something custom. I mean I'm going to save that and just make sure things are looking well in uh in my terminal here and everything looks okay. And now let's go ahead and when I refresh this we should see something different for banner CTA. And you can see it actually changed to something custom. It got rid of the idea,

5:24

got rid of the metadata, got rid of all sorts of stuff. So we've actually overwritten it entirely. Now that value is going to be the page itself. So we can use things like page. url. That's what we'd use in a template. Or sometimes in a template you'd use value. url or self. url depending on whether you're using a page or a stream field. This one we're just calling it value. But we could just as easily call it page. And so let's go ahead and return a dictionary in here. So we're gonna give this an ID. We're not gonna give it an ID, we're going to pass it back an ID of page. id. Let's give it a title, the page. title. And because this is a Wagtail page, This could be just about anything, any

6:10

any field that comes on a Wagtail page. So I'm I'm literally just in the Wagtail source code here. And so I can see I've got a title, a draft title, uh slug. So maybe I'm gonna want to uh return that as well, but probably not. Let's return an owner and let's return to do uh first published at. Might as well, if there is a first published ad. Is that it? Yeah, let's just do those two. So first publish. page. first publish. And the owner is going to be the page. owner, which is going to be a foreign key to a user. And let's just go clean that up. Make sure terminal land is happy. And let's refresh this. And I did something wrong. Object type of user is not serializable.

6:56

Okay, so that would actually require its own serializer. So let's go ahead and just get rid of that. Now I know that owner. is a user. Where are we? Boop, boop, boop, boop, boop. Owner is in here somewhere. I think I passed it. Owner. There you are. It's a foreign key to whatever the user model is. So in fact we could do page. owner. username and let's just try to traverse this just to make this really work. Okay, so there we go, and if I scroll on down, banner CTA. Uh the page is idea for the title is called blog, the first published at date, so we could also custom serialize this date as well, and the owner is username Caleb. That's me. So now let's take a look at this versus what we originally had. And all I did there was add a custom serializer. So what I'm going to do is

7:41

I'm just gonna comment that out. And I'm gonna open this up in a new tab. Now if I scroll down here, we're going to see the original one. The original one had an idea for it had the title, so both useful things. A detail URL for the API URL, that might be useful for you as well. You might want to pass that back as well. The type, not so useful to me personally, but might be useful for your application, so you might want to pass that back as well. But what we ended up saying was, hey, actually scratch all that, and hey, pass in the ID, the title, first publish that, and the owner. Now I'm gonna just undo that quickly. And there's actually one more thing I want to sh not to show you, but to put in there just to make this actually useful.

8:27

I'm gonna throw the slug in here as well, page. slug and The URL of this page, the exact URL of this page, is going to be page. url. So save that and refresh. Just wait for our server, Django server, to reload here. And our banner CTA goes to a page ID4 title blog first published at owner is Caleb, the slug is Caleb, and the URL is slash blog slash. That is the exact URL to the page that I'm looking for. So that is all there really is to serializing a foreign key to a page. We just pass in a custom serializer. It's a Django Rest framework field. And we just customize the two representation. Now you can do any sort of logic in here as well. If you wanted to do

9:13

a listing page, for example, and return A list of dictionaries or basically a query set of all of your blog pages or something. You could do that. You could perform any logic in here. You can do anything you want as long as you're returning something useful as a string, a list, a dictionary, something that you want returned in your API response. Now as always you can find this source code in the commit link down below. And if you're looking for more headless tutorials, you can go to learnwagtail. com Click this little search icon up here. Nope, that was the wrong one. You can click this little search icon up here, type in the word headless, and you will find all the other videos that are currently available when it comes to making your Wagtail website a headless content management system. Thanks for tuning in, I'm Caleb Tollin, and I'll see you in the next one.

Questions this talk answers

Why is returning only a Wagtail page foreign-key ID inefficient in an API?

The client has to make a second API request to fetch the referenced page, adding server and browser processing and increasing the user's wait time. A custom representation can include the needed page data in the original response.

Discussed at 1:32

How do I serialize a foreign key to a Wagtail page with a custom serializer?

Define a Django REST Framework `Field` subclass, implement its `to_representation()` method, and set that class as the `serializer` on the Wagtail API field. The method receives the referenced page and returns the representation you want in the API response.

Discussed at 3:06

How do I include a referenced Wagtail page's URL and other fields in the API response?

Inside `to_representation()`, read fields from the referenced page, such as `page.id`, `page.title`, `page.slug`, and `page.url`, and place them in the returned dictionary. Related objects may need their own serialization or a scalar field such as `page.owner.username`.

Discussed at 8:27

What can a custom Wagtail foreign-key serializer return?

It can return any useful serializable value, such as a string, list, or dictionary, and can contain arbitrary logic. For example, the talk returns a dictionary containing the page ID, title, publication date, owner username, slug, and URL.

Discussed at 9:13

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Kalob Taulien

More videos from Wagtail CMS