How to use Orderables in Wagtail CMS

This video features Kalob Taulien at Wagtail CMS 2023 .

How to use Orderables in Wagtail CMS
0:22:42
Published November 29, 2023
15,707 views
242 likes

Discover how to use Djangos Inline Models within a Wagtail Page in a feature called Orderable. Orderables let you add moveable content to your page without needing a StreamField. In this video, we'll create a Bootstrap 4 Image Gallery on our Home Page model using an Orderable.

The Learn Wagtail tutorial: https://learnwagtail.com/tutorials/how-use-orderables-wagtail-cms/

The Gist of it: https://gist.github.com/KalobTaulien/a72f6c4757be4283bbd7079756062812

The Full Git Commit: https://github.com/CodingForEverybody/learn-wagtail/commit/e280dfac976984972facde4589e84a72329d9290

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

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

Summary

Orderables in Wagtail are inline models for repeated, structured content that belongs to a specific page, making them a better fit than StreamFields for cases such as image carousels or related posts. The speaker builds a homepage carousel with a `ParentalKey`, an image chooser, an `InlinePanel`, and a `MultiFieldPanel`, then limits it to one to five images and renders it in a Bootstrap carousel template. They also show how to access the related items with `.all`, generate image renditions, preserve ordering, and mark the first carousel item as active.

Key takeaways

  • Use an orderable instead of a StreamField when repeated content has a specific purpose, fixed markup, and belongs to one page.
  • Define the relationship with a `ParentalKey` and expose it in the editor through an `InlinePanel`.
  • Use `min_num`, `max_num`, and a custom label to control how many items editors can add.
  • Group related editor fields with `MultiFieldPanel` to make the Wagtail interface clearer.
  • In templates, loop over the related manager with `.all`, render image renditions, and make the first Bootstrap carousel item active.

Summarised automatically from the transcript.

Transcript

3,979 words · auto-generated Show

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

0:00

Hello, welcome back. In the last lesson, last couple of lessons, we looked at adding stream fields, a bunch of different types of stream fields, and we even looked at how they are stored in the database. Now stream fields are great because you can move content around and theoretically and actually realistically you can add multiple stream fields wherever you want. So if we open up home models. py and we wanted to add another type of stream field somewhere on the page for let's say related blog posts or just a particular section uh in our template so let's open up our template let's go Homepage. html so we could have a set of stream fields in here and we could have a set of stream fields in here and we could have a set of stream fields inside of the banner. We could do all of that But that's actually not really how we're supposed to be using stream fields.

0:48

There's a better way. It's a slightly more complicated way, but there is a better way. Now I'm using the word complicated Pretty lightly, because it's actually not very complicated at all. And that's one of the beautiful things behind Wagtail CMS is that things are not complicated. Things are really, really easy for us. So for example, you know when you're reading a blog and at the bottom or on the side it's like related blog posts. Or you're on a website and it has a bunch of images and there's a carousel and the images swipe left all the time or right all the time or something like that. That is not a good use for a stream field. Instead, we would use something called an orderable. And in this lesson, we're going to learn all about orderables. Well, we're going to learn mostly about orderables.

1:35

And you're going to get a good understanding of how they work, and then you'll be able to extend it as much as you like, as as much as you really need. So we're actually not going to create the blog post example because we don't have a blog yet, but let's create an orderable where Someone can upload a minimum of one image, but also a maximum of, let's say, five images for an image carousel. And we're going to put that on our flex page. Hmm, no, we're not gonna put that on our flex page. In fact, we're actually gonna put that on our home page. Now the reason that we're not going to put it on the flex page is because the flex page is supposed to be flexible. The homepage, however, has a little bit of flexible content, so it's an it's got an optional stream field in here, and there might be a couple more down the road.

2:23

But if we wanted to have some sort of image gallery as the banner, for instance, we could do that. Now we're not going to replace our banner entirely. Uh just because that's going to be a lot of work for just one video. But by the end of this video you will have a very good idea of how to actually implement that. So an orderable, if you come from a Django background, which you probably do, is basically just it's an inline model, that's all it is. But Wagtail makes this really, really nice for us. So on our homepage we're going to add a new class. The class name is just whatever it's going to be called in the database as the table name. So let's call this one homepage carousel images or something like that.

3:09

It's going to inherit from an orderable, which does not exist. So let's go and make that exist. So I know that that comes from Wake Till Core models, so let's import orderable. And now give it a little doc string. between one and five images for the homepage carousel carouself carousel Now this does not look like a regular Wagtail page, but if you're familiar with Django models, it's not super unfamiliar either. So the one thing we need to do here is we need to explicitly say which page is this connected to. So let's let's add a page in here. Page is going to be parenty. And it's going to be home, home page.

3:57

And I'll explain this in just a moment. Related name is equal to carousel. images. We need to import that. And we get this one from a package called model cluster, which comes with Wagtail, so you don't need to install anything additional. So let's do from model cluster dot fields import Parental key. Now what this parental key is basically saying is what is this inline model relating to? Which model is this going to be an inline form. And we're saying the home app and the class home page, which is down here. And the related name is how we're actually going to create the inline panel because everything has to be in panels in Wagtail And then we're also going to use that exact same related name in the template to create our carousel.

4:46

So now we need to add fields, because the page is not necessarily a field. It's just saying, well the the parent page The page that we want to be inlining to is going to be the homepage. That's all that one does. So once that's written, basically you can ignore that for now. And then we want to add an image. Now we know how to add an image, and in fact, we can just copy and paste banner image altogether, because that's not that different. And we're just gonna call it let's not call this image. That's going to be a crazy not naming conflict, but that's going to get confusing. So let's call this Carousel image. That's it. Now it's a little close to carousel images.

5:33

This is going to be our loop down the road, and this is going to be the item that we're trying to get. Can this be null? Sure. Can it be blank? No. On delete, set null, related name. Cool. Everything there looks fine Now, because again, this is a Wagtail inline panel, this isn't orderable, we also need to add panels. So we do panels is equal to, and then we just give it a panel. So this one could be a field panel if it was a regular text field, but this one's going to be an image chooser panel because it's an image choosing field So carousel image, and I just got that from up here on line 17. Clean up that a little bit. Okay, so I've just opened up my terminal just to make sure there's no errors or anything, and it says,

6:21

got an unexpected keyword, argument related, named. And if you're screaming at the screen saying, Caleb, you spelt it wrong, good catch. It happens from time to time and that's why we have a terminal basically saying, oh hey, there you go. This one is saying related named. Okay, so I just restarted Django and this should be working as expected now. Not going to, but you you would think it would. So let's edit our homepage. And so everything looks normal still. But if we were to stop and run migrations, for instance. Python 3 manage. py make migrations. It said created model homepage carousel images.

7:07

Okay, interesting. Python 3 manage. py migrate. And then lastly python3manage. py, run server. Refresh our page in our admin and still again nothing's going to happen. Now if you're scratching your head thinking, well why is nothing different in here? Well that's actually pretty simple and we've run into this a number of times now and it's simply because we don't have another content panel in here. So let's add one more content panel and this one is going to be simply the related name, the inline name that we want to use. And this is called carousel images. So I throw that in there. It's not a stream field panel. It is a inline panel. All of a sudden, VS Code is like, mmm, I don't know what that is, so uh

7:55

we're gonna need to go and import that. So I know that we can import it from Wagtail, admin, edit, handlers. And let's go in here and put inline not page inline panel. And let's see, are you still complaining? Nope. Okay, so Uh let's just open up our terminal. Okay, everything is okay. Now we can get there. It's taken us a little while, but we've gotten there. And now we have this little add button in here, and it just says add. And we can add as many as we want. We haven't told it to limit, but we can see that there's a carousel image field in here, which we've added with an image chooser. And the nice thing about orderables is they are orderable. We can move these up and down.

8:40

It's not a problem. We can delete them. We can set a minimum number, a maximum number. But the first thing we need to do is actually we need to put this into something that's a little more manageable. Because if someone was to see this right now on their page, they'll go, uh, there's a title here, banner title, banner subtitle. But there's nothing here. What's the deal with that? So to get around this, we use this thing called a multi-field panel. And all that does is basically says there's a bunch of panels and they're all related to each other. And this is very specifically just for display. So we're going to add a multi-field panel, and it's going to take a list of panels And it also has a parameter in here called heading. So let's put carousel

9:26

images in here as the heading. And let's move this down one line. It's gonna complain that multi-field panel does not exist, so let's go and add that. Multi-field panel. Any complaints? No complaints Refresh our page and we will see that there is now a header. Carousel images. That's really nice. And in fact, what we can do is we can actually move all of our banner stuff together now because every banner section here has its own little title. Sometimes that's not really what you want. Sometimes you want to be a little more organized than that. Actually for me, all times I want to be more organized than that. So I'm going to actually do that right now. It's super, super quick. So again, all you have to do is write

10:13

Multi-field panel, it takes a list and it has a heading called banner options. That's what we're gonna call it So we're gonna move the banner title, subtitle, banner image, banner CTA. And I'm just gonna cut those out, put them in there. And so I've got a multi-field panel with all of my banner stuff in there. We've got a stream field panel with our content for our stream fields, and we have a multi-field panel for our carousel images. Now for me this does not make sense to have these carousel images below the Stream fields, so I'm gonna actually put them below the banner, but above the stream fields. And make sure we've got our commas in place Refresh our page and we're going to see that there's a little bit of a difference in here now.

11:02

So we have banner options and now we have banner title, banner subtitle, image, and banner CTA all under one section That's the beauty of a multi-field panel. Carousel Images, it has its own title now. Now we actually have some sort of context of what this is about. But add doesn't make sense. We also want to limit this and we want to set a minimum number of images that there has to be at all given times. So in our inline panel we can say Max num is equal to 5, min num is equal to 1, and the label for that button is going to be Well, what are we adding? We're gonna add an image. And so that label says add image right here.

11:47

When I refresh the page, always has a minimum of one in here You can still delete it, but if you were to try to save the page, it would say you're missing one. And let's see how many we can have. So we've got two, three, four. 5 and then that button automatically disables itself. So you can't add more than 5. It's really, really, really nice. And now if we wanted to add all these different images, all we have to do is select different images. So for instance, I'm going to choose this image, and I'm going to choose another image, and I'm going to choose another image. These are actually probably going to be terrible, terrible images. So I want three in there right now. And I'm going to publish this page. Now this is the home page, and when I view live, we're not going to see anything again, and that's because we have to go into the template and explicitly say, we want

12:34

to show these. Now if you come from a WordPress background, you're going to be like, well, why don't they just automatically show up? But if you come from a Django background. or a slightly more senior developer background, you were thinking, oh, this is actually really good because now the client the client doesn't have too much control. And for any particular feature that they want to implement, they actually have to think about it. They can't just say, oh, I want this, this, this, this, and this, and then all of a sudden their site is really, really slow and bogged down and ugly. They actually have to think about it, which is really, really good for the internet entirely. So now we have a banner here and we have stream fields here and let's say I wanted to put a carousel right in between. How do I go about doing that?

13:20

Well the first thing I need to do is I need to go to getbootstrap. com and head over to documentation and I want a carousel. And because they have sample code, so first slide that's not more than one. Uh second slide, this is something that we want. This is exactly what we want. Uh oh, and we could even extend it with this. Uh so maybe we start Maybe we just start with the simple one, because we have just the image, and then as a fun experiment, what you can do is you can then extend that to have a title and some text and use a different carousel if you wanted to Now I'm just gonna copy this to my clipboard because I'm being lazy.

14:07

And I'm gonna open up homepage. html. So I have my banner stuff in here. I have my stream fields in here, and Right in the middle, I'm going to give myself a lot of room to work. Simply paste that in there. Now when I refresh our page. We can see that there is something in there. Basically it's just missing images in a carousel. So we know that the carousel works, so that's that's the front end essentially taken care of. Or at least the bootstrap part of it is taken care of. We see that we have images in here, so we are going to want to loop through our images and replace these. And well, honestly, that's about it. So let's do that now. Let's do A little for

14:52

loop. Uh da da da. This indenting is gonna drive me nuts. I'll fix that up after this video behind the scenes. To loop through these is actually quite simple. So we do for Let's do just we'll call this cycle for now. So for like a loop cycle in and what we're referencing here is carousel images. Now it's not like adding context We actually still need self because self is now basically we said that this has a model inside of a model, so we're going to use self. carousol images. Okay, so we've got a for loop in here, we've got an end for loop in here, and let's

15:37

Let's just cut that out, throw that in there, and get rid of these. So now let's refresh and again we're still not going to see our images. But we see this deferring related manager. Object is not iterable. What that is saying is, oh, actually I can't iterate through a class. I need to iterate through all the items in a class You're going to run into this time and time again. It's very, very common to miss out on this one. But in the template, all you do is dot all. So now we need to add our image in here. So let's create an image rendition. So let's use the Wagtail image tag. We're going to use the loop cycle dot because we're now inside of our carousel, our inline model, which is basically like looping through a stream

16:25

a stream field. We need to access the carousel image. We're going to feed it the image that it's been given and we're going to fill it with 900 by 400 or something like that. These images are going to be terrible, by the way. Um, I'm going to put image. alt. Oh, by the way, there's an image. tag that will actually create the image for you and you can add it uh add classes and everything. I like to be explicit like this because sometimes I like to add a lot of stuff to images, a lot of extra classes or sometimes IDs or you know front-endy stuff anyways. Image dot url and let's go and refresh our page and see what happens.

17:14

Hello, we have a carousel. But for whatever reason we can't click Now we know that this was working before, so why is it not working now? Well we can deduct this. We can say, well, it was working before. We added our loop in there and it's not working. So it's got to be something inside of our loop. What could it possibly be? And we have to deduct a little bit here. And we have to think, well, it was working before. We did see something move, but we added our stuff and it's not working now. Two things we can do is we can always inspect, and we can see that we've got an item here and we've got three or two other items in there So it can't be that. Things are working. The loop is definitely there. So to uh deduct

18:00

or reduce our problem is we have to look at this. inside of our loop. So we know our loop is working. We know that there's an image in there. If we get rid of active, what happens? Now I already know the solution to this. This is not going to fix anything for us. But in Bootstrap, your first one should always be active, your first item. So all we're going to do is check the for loop counter. So if for loop Not for loop, if for loop dot counter is equal to zero I'll move that over so you can see it. And by zero I meant one. And if. And I like to Be a little

18:46

finicky about my spacing. So refresh and guess what? This is going to work. So we have Our first lady here, we have a co-working space, and we have our second lady there. And in fact, this is the order that we're looking for. So we've got uh the first lady, and then we've got the co-working space, and then we've got the second lady. And if we ever wanted to rearrange these, so let's say we wanted that co-working space first, and then maybe pictures of people who work in that co-working space. We just save the page and refresh and this shows our co-working space and we now have a carousel. Just like that So that's all there is to orderables. Now if you're thinking to yourself, why would I use an orderable instead of a stream field?

19:33

Well you can. You can use a stream field. But an orderable is a better way to go in this particular instance because stream fields can be unlimited. We learned about stream field list blocks, but A list block doesn't have a particular limit. Maybe it does in the future. Maybe in a future version of Wagtail, you'll be able to say, oh, I only want a list block to show up five times. But even then, you're still limited because that list block has to show up inside of your stream field for loop. Now you could add another stream field. in your page class. So instead of content it could be like here's content one and also here's content two Two. You could do something like that, but now you're just adding columns to a database that don't necessarily need to be there, just to add one particular thing.

20:24

You could do this in a much more concentrated manner. And we achieve this with our orderable. Now the nice thing about orderables is you can place them anywhere you anywhere you want. So for instance Our orderable belongs inside of a carousel, and that only belongs on the home page. It doesn't belong to any other particular page. It is a one-time instance on this page, but it is repeated content. So people can select different images, yes, but our markup never changes. It's going to be the exact same every single time. So it is very similar to a stream field, however, it is more of a concentrated or a specific use case So for instance, at the bottom of a blog post, if you wanted to select related blog posts or featured blog posts or something like that, you

21:13

could do that. You could add an orderable and you could just simply select the page. So instead of image and instead of wagtailimages. image, you would do Wagtailcore. page, and then you would just loop through related post, and then you just loop through that. You'd also have to change your related name, obviously. But that's really all there is to it. So in this video we have learned what the difference between stream fields and orderables are. why you should use an orderable over a stream field in particular instances, and we actually made an orderable work inside of a carousel. And we even debugged a little uh bootstrap thing where a little bootstrap problem where active was actually holding us back.

22:00

Now don't forget, I am Caleb Tallin. I am the author of this video. I'm an author on learnwagtail. com where you can learn. Lots of cool things about Wagtail. All the videos for this Wagtail course are also available on learnwagtail. com along with tons of other tutorials. If you like this video, feel free to subscribe, thumbs up, comment. And if you ever have further questions, there is a Slack community in case you wanted to join it, but there's also docs. wagtail. io. There's Stack Overflow questions. There's LearnWagtail. com. There's a lot of resources out there to help you. So hopefully this video was helpful in helping you understand the difference between stream fields and orderables and why you should use an orderable.

Questions this talk answers

What is an Orderable in Wagtail?

An Orderable is essentially a Django inline model managed by Wagtail, allowing repeated, reorderable content to be attached to a specific parent page. It is useful for things like carousel images or related posts.

Discussed at 2:23

How do I limit the number of items in a Wagtail InlinePanel?

Set `max_num` and `min_num` on the InlinePanel. The example requires at least one image and allows no more than five, and `label` changes the add button text to “Add image.”

Discussed at 11:02

How do I display Wagtail Orderable items in a template?

Loop over the related manager with `.all()`, such as `self.carousel_images.all`, and access each Orderable’s image field inside the loop. The example uses Wagtail’s image tag or image URL to render each carousel image.

Discussed at 15:37

Why should I use an Orderable instead of a StreamField in Wagtail?

An Orderable is better for a specific, repeated piece of content with fixed markup and a controlled number of items, such as a carousel or related posts. StreamFields are more flexible and potentially unlimited, but using one for this kind of focused feature can add unnecessary fields or database columns.

Discussed at 19:33

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