Headless CMS: Exposing Orderable Data and StreamFields

This video is from Wagtail CMS 2023 .

Headless CMS: Exposing Orderable Data and StreamFields
0:17:22
Published November 29, 2023
3,160 views

In this tutorial you will learn how to add a Wagtail Orderable model fields to your Wagtail v2 API, and how to add StreamFields to your API response. As with everything in Wagtail, this is a simple task for developers.

Tutorial: https://learnwagtail.com/tutorials/headless-cms-exposing-orderable-data-and-streamfields/

Learn Wagtail from scratch with the official Wagtail for Beginners Course
https://learnwagtail.com/wagtail-for-beginners/

The Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/9735b934d10fd07b837becd4412838569b355f13

Used in this video: Wagtail 2.4, Python 3.7, Django 2.1.5 #Wagtail #Django #Python

Summary

Wagtail’s API can expose StreamFields and orderable child objects by adding `APIField` entries to the relevant page and orderable models. For orderables, the parent exposes the related name, while the orderable itself must expose the fields that should appear in the API; StreamFields can be exposed directly by adding their field name. Foreign keys to ordinary Django models do not automatically provide useful serialized data: instead, add properties to the orderable that return the related model’s values, then expose those properties with `APIField`. Image objects require additional serialization because they are not JSON serializable by default.

Key takeaways

  • Add the orderable’s related name to the parent page’s API fields, then add API fields to the orderable itself for the data to expose.
  • Expose a StreamField by adding its field name to the page model’s API fields.
  • Foreign keys to plain Django models may return only IDs and metadata rather than useful nested content.
  • Create properties on the orderable to return values such as an author’s name or website, and expose those properties with APIField.
  • Related image objects need a custom serialization approach because they are not JSON serializable by default.

Summarised automatically from the transcript.

Transcript

3,056 words · auto-generated Show

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

0:00

Hello and welcome back to another lesson on learning Wagtail. In this video, we are going to enable an orderable and a stream field into our API So in the previous videos what we did was we got the API up and running We added some custom fields and now we want to add our stream fields and maybe something a little more advanced like an orderable So before anything, what I have to do is go into my website, get into my environment. You may not be using pipend if you might be using something else. Either way, you're going to need to get into your environment. And then we just type manage. py run server. Alright, our server is running. And if you open up your browser and go on over to localhost

0:48

port 8000 slash API slash V2 slash pages. You will see we have our API up and running. Now if you don't have an API up and running, we are using Wagtails Headless CMS API settings. You can find a video on that in the uh in the rest of the YouTube playlist. I covered it. I covered that subject about installing it and getting up and running over the last two or three videos, I believe. So what we're looking at here is we have an ID of 3, an ID of 4, we've got an ID of 5. So these are all different pages. They give us the different page type, detail URL, HTML URL, slug, first published ad, a bunch of other good things The one we want to work with is our homepage. So let's open up the detail page. And all we do here is we click on the detail URL, and that's going to bring us to the detail page.

1:35

All it really did was add it a three in the URL. So we can see on this page that we have a bunch of extra information. We've already enabled banner title, banner subtitle, and banner image, and also banner CTA. But if we open up our code, where is home? And then let's go into models. py. Oh, that is far too large. Okay, we're in models. py and we have an orderable in here and then we have our homepage and if I scroll on down We have our exposed API fields. Now let's say we wanted to add an orderable to this. So we have an orderable right here. And because it's an orderable, it's basically an inline

2:22

model, is the way you can think of it. We're going to grab the related name, and we are going to. Come back down to our home page and add the related name in here. Now this is not going to work the way you think it's going to work. I'm going to save this and I'm going to refresh this page. So it gives us our carousel images. This is nice, but it doesn't give us any extra data. And the reason for that is because we actually have to add one more API field. We have to add one on the orderable itself. And this works just like panels. Anywhere you see a panel is a place where you can add An API field. So I'm gonna scroll that down, type in API underscore fields, it's equal to a list, and the only thing I want to expose here is this carousel

3:13

image. And this is going to take an API field with carousel image. So this one is going to expose the actual image itself, but we use a related name in the home page So if we save this and refresh our page here, we will now see that we have type detail URL, download URL, and a title. And then because this is not the greatest example in the world, If we had another field in here, and for this orderable, maybe we had a different title, because it's a carousel, or maybe a CTA in here, all we would have to do is add another line in here, and this would be Different field name. Whatever the other field name is that's that's somewhere around this area, you would just add it to this list.

4:02

And all of a sudden it works. It just works. It's all actually, it's quite magical. I'm not gonna lie. It's totally magical. If you come from an ecosystem like Node, you have to do a lot more work to get this up and running. But with Wagtail and Django, it is super, super easy. Okay, so that is adding an orderable to your API. Now let's go ahead and. Scroll down. Where are you here? This is hard to read, so I'm going to put this on a different line. Please hold. So I have a stream field here, and inside of it I have a list of tuples. Well, there's only a single tuple in there right now, but theoretically I could have more like that. I have a stream field and I want to expose all of my stream fields.

4:50

So this might not be again the greatest example because there's only one in here, but all we have to do is add API field and then our name. We didn't cover this before. We're going to cover that now. So literally all I did was added one extra line here and said, oh, use that stream field, please. Head on over back to our browser and refresh. And it looks like nothing happened, but if we scroll down, we will see our content. Our type matches this name in here. It gives us value, so it has a title, text uh button page so we know that that's the ID of the page that it's going to, a button URL and some button text.

5:35

Button URL is blank because it can be blank. The button page is selected and it has a custom stream field ID. Now all of these stream fields are basically a giant list, or if you're in the JavaScript world, that is an array of objects or a list of dictionaries. Now this may have been a little bit too fast to pick up on, so let's run through one more example. Let's grab let's grab a page In fact, let's uh let's go and explore. It's figure out what page we want to expose here. So we have pages Home blog. Uh let's do this one? This one?

6:20

It's gotta be one of these. Hello world. Oh, we got some authors. That's a good one. Okay, so this is what we're going to do is we are going to expose some fields for our blog post page. And the blog post that we want to edit is blog post number six. So let's go through here. We see idea 6, blog post page, blog post 1, and let's click that detail URL. Also just a note, if you are using These links where it says HTML URL and Detail URL and you notice that it doesn't have the port that you're looking for, you can just go into your Wagtail settings, go into sites, yes, leave page. Go into localhost and change that port. By default, it's set to 80. Just set it to 8000 or whatever port you're running it on. And that will automatically fix it up in

7:08

here. Okay, so we are looking at a blog detail page. We have a bunch of metadata, we've got uh parent information, and we have a title. Let's go take a look at what this is showing in our models. So in our models, I'm just going to scroll to the top here. So we have a blog authors orderable. We have a blog author We have da da da da da da a blog category, which is registered as a snippet And a blog listing page. Doo doo doo. And if we go down, a blog detail page. And we've got a couple other detail pages that are inheriting from blog detail page. But this is the one that we want to work with right now.

7:53

So I'm gonna scroll down, find our content panels. We got a bunch of content panels in here, but we don't have any API fields. API fields It's equal to a list, but you're going to notice that if you add your API fields here, it's not going to do anything. And if we head on over to our homemodels. py, we are actually importing this line here, so line 6 And that is literally the only thing that we are doing. We're just importing API field. So I'm just gonna take a mental note that I'm on line 220. Come back up here and let's throw that right there. Wagtail API import API field and let's go back down to line 220. So I have these API fields and what do I want to enable in here?

8:41

Well I probably want our content, uh probably categories, banner, image. Banner title and we're going to see that if we look at the content panels, we actually have inline panels here. We've got actually we just have the one inline panel, but we have blog authors in here. So we're going to want to add that as well So I'm probably not going to cover every single field, but let's definitely take a look at the orderables and the stream field. So we have blog authors in here, let's go ahead and add a API field We'll call it blog authors. And that is a related name, so remember we're going to have to go back to our blog authors and enable more API fields And then let's also add our Stream Fields. API field, and put content in there.

9:29

In your application, that might be called body, that might be called Stream Fields. I simply have it called content. Now when I refresh this page, we can see that we have blog authors is not returning as much data as I would like it to return, so we'll talk about that in a moment. And we have content in here. We have a full rich text stream field The value is in straight HTML and we have a custom ID. So great. Our progressive web application or single-page application is now able to use that content. But it can't use that blog author because Well unless we look up every single blog author by its ID, which actually we can't, because if we went to two pages slash one, you're going to see it doesn't even exist. This is the blog author ID from the orderable. So now we head on over to our code again and we need to look for this blog authors.

10:19

So we come up, where are we? Blog authors, you gotta be in here somewhere. Blog listing page, blog category. Okay, so we got blog author and then at the top we have our orderable. And okay, so we have an author which is going to go to blog. author. We can see that class here, blog author. So we have access to the name, the website, and the image. And that all comes through one field called author. So let's scroll that up so you can see which class we're working on. Let's add an API field. API fields. equal to a list, and that list is simply going to be API field author And if I refresh our page, we can see that our blog authors is now going to show us that there is an author field in here, and that's a foreign key.

11:08

You can tell because it has an ID. And it also has meta information. The blog author is the class type. And you can see that there are two authors in here. So at this point in time, you're probably thinking, well, there's still no useful information in here. And actually, you are absolutely correct. There is no useful information in here at all. In fact, all it's doing is grabbing an orderable. We're grabbing a field, and it's saying that it's a foreign key to another area, another class somewhere down the line called blog. blog author. So let's go ahead And take a look at our blog author model. Now, you might be expecting to simply just add your API fields to your blog, to your blog author model. However This is simply a Django model.

11:54

It has no idea that there are API fields. Now there are some additional niceties that Wagtail gives us like panels. And because there are panels here, like I've been saying before, you can assume that there are API fields in here. And I'm going to show you that this does not actually work the way that we're expecting it to work. So we have a name a blog author name. Put that in there and let's refresh our page. We can see that nothing happens. Now this is not behavior that you're expecting because we've just been sort of willy-nilly throwing API fields all over the place. But it doesn't work in every single instance. And again, the reason for that is because this is a Django model. You can tell that it's Django model

12:39

because where it says models. If we come up here, from Django. db, import models. So what we're going to do instead is on our blog authors orderable, we're going to add a custom property and expose that instead. So we can add a property in here. Property. And let 's give this, I'm gonna move this down a little bit. Let's give this a name of uh we don't want author because that's already exposed, but we want to get the author name, the author website, and the author image. So let's Do one example here where we're getting the author name. Author name and it's going to take self and all it's going to do is return self. author dot name. Now if you're wondering where I got that from

13:26

Self is, well it's because it's object-oriented programming, so it's referring to this entire class. Author is the field name, and because it's a foreign key, Django allows us to use dot notation in order to grab the name. And to expose this, all we have to do is type in author name as an API field. Now let's go back here, refresh. And we have an author name. Now you notice it's not nested underneath author, and that's because author is its own field. You can think of this as sort of being hidden. Author is its own field, and author name is its own field, as we can see here and here. So let's say we also wanted to get the website and we also wanted to get the image. So I'm gonna call this one website.

14:11

I'm gonna call this one image and we are going to copy this property two more times. So author website, author image, author website, and author image. Save that and let's see what this is going to give us. Now we got rid of author, but we have author name. We also have author website and author image. And we can actually see that we ran into an issue here where the object type of image is not JSON serializable We're actually not going to cover that in this lesson. We're going to cover that in, I believe it's the next lesson I'm going to be making. So what I'm going to do is I'm going to actually undo that one. So I apologize for the inconvenience there, but that one deserves its own video.

15:00

So I just got rid of that and refreshed the page and scroll on down. We've got an author name and we have an author website. Beauty. So one takeaway from this is when you are working with API fields on any class and you notice that's not working, chances are it's inheriting just from a Django model. And you're probably using some sort of foreign key system where. In this example we have our author, which is using the blog app. and a class called blog author, which just happens to be right below. Now that's a foreign key that we can grab these fields on. But because it's a Django model, we cannot just throw API fields on there.

15:46

Yes, it works with panels, and it holds mostly true. Wherever you see panels, you can most most of the time you can put API fields But when it comes from something raw like Django, we actually need to do a little extra. And that little extra is actually super, super simple. All we did was we added a function or a method in our class. We said, actually, read this as a property, and it just returned the name and the website, and then we exposed those. And that is it. And lastly, we ran into an error where the image was not JSON ifiable. And we're actually going to take a look at that in the next lesson. And that's getting its own video because it deserves its own video. Now don't forget at any point in time you can reference this code on GitHub. It is all available there from the very first episode to

16:35

well, the very last episode. All of this code is available in GitHub in separate commits. My name is Caleb Tallin. I'm the voice behind the videos. I'm an author on learnwagtail. com. If you're interested in more videos like this, you can always find these videos available on learnwagtail. com or you can find them available on YouTube. I'll link up the playlist in the top right corner And hey, don't forget, if you learned something new in this video, if you found something was helpful, you can always share this video. You can subscribe, which is always really appreciated. Or if you just want to pop your head in and say hi, you can leave a comment down below. Thanks again for watching, and I'll see you in the next video about custom serializers.

Questions this talk answers

How do I expose an orderable in the Wagtail API?

Add the orderable’s related name to the parent page’s API fields, then define API fields on the orderable itself for the data you want returned, such as its image or title.

Discussed at 2:22

How do I expose a StreamField in the Wagtail API?

Add an API field using the StreamField’s name to the page model. The API then returns the stream’s blocks, values, and custom IDs.

Discussed at 4:50

How do I expose blog authors and StreamField content on a Wagtail blog page API?

Add the related blog authors field and the content StreamField to the blog page’s API fields. The StreamField becomes available directly, while the related authors need additional fields exposed on their orderable.

Discussed at 8:41

How do I expose fields from a foreign-key Django model in the Wagtail API?

API fields added directly to a plain Django model do not work automatically. Instead, add properties to the orderable that return values such as the related author’s name or website, then list those property names in the orderable’s API fields.

Discussed at 12:39

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 from Wagtail CMS