How to Add a Basic StreamField to your Wagtail CMS Page

This video is from Wagtail CMS 2023 .

How to Add a Basic StreamField to your Wagtail CMS Page
0:19:34
Published November 29, 2023
28,153 views
283 likes

In this lesson we are going to learn how to add a basic StreamField to a a generic Wagtail CMS Page. We'll create a new app from scratch, and this StreamField will have a title and text (using StructBlock), a custom template, and it will lay the foundation for the next lesson which covers inheriting RichTextBlock and modifying the features it can have.

Tutorial Link: https://learnwagtail.com/tutorials/how-add-basic-streamfield-your-wagtail-cms-page/

Git Commit (includes Part 2): https://github.com/CodingForEverybody/learn-wagtail/commit/84ad12e1b69343f192a44359ca0f7b19fe681ca7

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

Wagtail StreamField lets editors build a page from movable content blocks instead of fixed sections. The speaker creates a `streams` app and `blocks.py`, defines a `StructBlock` containing required title and text fields, adds it to a page model with `StreamField` and `StreamFieldPanel`, and runs migrations with the field optional for existing pages. A template loop using Wagtail’s `include_block` tag renders each block through its assigned template, allowing editors to add, reorder, and move sections within the content area.

Key takeaways

  • StreamFields allow content editors to arrange page sections in any order rather than entering content into fixed fields.
  • Custom blocks can be defined in a separate `blocks.py` file using Wagtail’s `StructBlock`, `CharBlock`, and `TextBlock` classes.
  • A page model needs a `StreamField` and `StreamFieldPanel`, and the field should be made optional when adding it to pages that already exist.
  • Each block can specify its own template, icon, label, and help text.
  • A template `for` loop with `{% include_block block %}` renders the blocks in the order selected by the editor.

Summarised automatically from the transcript.

Transcript

3,380 words · auto-generated Show

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

0:00

Welcome to another video on Learn Wagtail. In this video, we are going to be talking about Streamfields Now stream fields are this idea that content does not need to be fixed in any particular content. So when you think of a header, a header is always at the top of your page, or a banner is always at the top of your page, and a footer is always at the bottom of your p And in between those sections you have different areas of content. And in a lot of instances, especially in static sites, those Content sections are not able to move. But what Wagtail has introduced a number of years ago is the idea of Stream fields and stream fields means basically you have one section

0:45

and you can move it around somewhere else. It doesn't necessarily have to be in one particular fixed location. And in fact WordPress 5 just came out not that long ago, and it introduced a visual editor called Gutenberg, and it does the same thing, so it only took several several years. But eventually even WordPress caught up. So this is clearly a very, very important feature for content editors So in this video, I'm going to go through a couple different types of stream fields. It's going to be relatively fast. I'm going to give you the code at the end of it. It's going to be in a single commit. and you will be able to basically get up and running with at least two stream fields if you just looked at the source code. Otherwise, if you're interested in learning, feel free to stick around and we are going to get started right now.

1:33

So the first thing we need, we actually need a new app. Well, actually we don't need a new app, but we're going to create a new app anyways. Because we need to create a new file and it could be called blocks. py, which is what we're going to call it, but it could live inside of home, it could live in flex, it could live in search, but because it's going to be accessed by several different pages, we're going to create a new app. So if you just open up your terminal, and I'm already in here, so I've already done my Pipand shell, I'm inside of my shell, and now all I want to do, python3 manage. py. Start app and the app that I want this to be called is Streams. That's it. Streams it could be stream fields. I just want to call it streams because it's nice and short and there's nothing else in our application that's going to be called streams.

2:20

It's a pretty unique name. Okay, and while we're in here, we might as well run server. Run server. Now if you head on over to your editor, and in my case that's Visual Studio Code, you can see that there is a new file in here, or folder rather, with a bunch of files called streams. Now we know that this is not going to actually do anything because even though the files are in there, it's not being instantiated by our application. So if you open up base. py and just like what we did with uh with our flex page, we're gonna do the same thing with streams. Alright now so the next thing I'm going to do is I'm going to open up streams

3:07

and honestly I don't need models I don't need tests and I don't need views I'm gonna keep them in there in case you ever want to do anything with those But right now those are are not really useful to us. I want to create a new file called blocks. So I'm going to create this new file called blocks. py And it doesn't have to be called blocks, but that's what I'm going to call it. It sort of fits with the terminology that Wagtail has been using as well for stream fields and blocks. So a stream field can have a class. And instead of calling it a model because it's more of a complex data type, we call them blocks. So we have a blocks. py file in there. I'm just going to close these other ones and I'm going to open up flex. And let's go into model. So we have our page in here and we have

3:53

this little to-do in here. To do add stream field. So I'm gonna get rid of that. I'm gonna uncomment this one out. And now you can see that it's immediately complaining because I have Flake8 installed that this is not defined. Basically this import is not in here. So uh let's import that right now. Now if you're ever wondering, and you probably are, uh Caleb, where do I get all of these imports from? You can always check out the Wagtail docs. They have tons of examples in there. And all the imports are included as well. So I'm going to do from Wagtail. core dot fields import stream field. And that's it. But now we have a new field in our model, and we also want this to be editable in Wagtail.

4:42

So Wagtail is going to give us a bunch of nice little goodies to automatically rearrange content for us. and we don't have to worry about doing any of that front end work. So what we're going to add here is not a field panel, because this is a special type of data. This isn't a regular char or text field. This is a stream field. And we'll see why this is a little bit uh different once we actually get into the admin. So just bear with me for a little bit here. So for this one we need a stream field panel. And we just need the name of our field. Now this name of your field does not need to be content. Often I've seen it as body instead of content. It's totally fine, whatever you want to call it, it's It's okay. I typically stip with content because it is the content of a page. Now if we save this, I also get an error saying, oh, Streamfield panel is not imported.

5:33

Or is not defined. So I'm going to throw this in here. This comes from WEG till I've been edit handlers. So Stream Field Panel. And it stops complaining. Now at this point in time, if you open up your terminal, you're going to see a bunch of errors. And basically that's saying stream field cannot be empty. We have to give it something. So we're actually going to do this in two steps. First, we need to create a stream field in our blocks. py. We need to import our blocks. py, that's all part of step one. And then step two, we simply add it as a list into our stream field here. So for our first one, let's do something relatively simple. So for our first one, let's create a very simple stream field. It's going to be a struct block.

6:19

And you'll understand what I mean in just a moment, because I'm throwing all this terminology at you. Don't expect that you you have to know it right now. You'll learn it as you go. So we're going to create a new stream field. It's going to have a title and it's going to have some text in it. And that's it. We're not going to use rich text because we might not actually want to give the content editors any ability to bold or italicize or use paragraphs or anything like that. It's just straight text. And there are a lot of cases where you are going to want to do that. So in our blocks. py, before we do anything, we are going to want to uh well I'm gonna add a doc string so stream fields live in here And then we're going to want to do a little import. So from Wagtell. core import blocks.

7:04

And this is where we got the word blocks from. This is why we're using blocks. py, so that the name and convention stays the same And all we're doing here is we're saying, hey Wagtail Core, can you give us that blocks file? And then we're going to access a bunch of classes inside of that. So please just give us that blocks. Py file. That's Wagtailsblocks. py, not our blocks. py. So that's the confusing thing about this name and convention is that there are two different blocks. py. Okay, next let's actually create our first stream field. So let's do class and uh what do we want to call this one? Maybe let's just call this text and Uh title and text as I had it written the first time, title and text block. And it's going to inherit from blocks

7:49

dot struct block And this is simply title and text and nothing else. Next we need a title, because we said this is going to have a title and text, so Let's add a title. Now this is not the same as adding fields to a Django model, because again, this is a complex data type. This is not just a simple variable character or a text field inside of a database. So instead of using models. char field, for instance, it would look like this. We are going to be using title is equal to blocks. charblock. So just replace models with blocks. as we can see there. And anytime you see field, just replace that with block.

8:39

Now this has a couple different parameters as you can see. VS Code has already given me a couple to fill in here. First one is required. Is this a required field? So this is like blank and null mixed together. Is this required? We're gonna say yes, this is absolutely required. And let's add some help text. Add your title. That's it. And then our text is going to be blocks. And instead of a text field, it's going to be a text block. Required, this is also going to be required and the help text is add additional text. So not super helpful help text, but there it is.

9:26

And then lastly, we need well we don't need to, but we should just for the sake of being very explicit. Let's add some metadata here. So we do class meta No QA, so please don't QA my line there. Template is equal to, and we're gonna throw this into streams and let's call this one title and text block dot html and then we can give it an icon Edit is the icon and then let's give it a label and this is all going to make a little bit more sense in just a moment. Title and text. We have one flake

10:11

eight error there. So let's go fix that up. No new line at end of file, so let's create a new line. There we go. Flake 8 is not complaining anymore. And we're also writing nicer code. So now we flip back to our flex slash models. py file and let's give this a list. Now this list actually takes a list of tuples. So we're gonna call this one title and text. This is just the name of our stream field. It doesn't really matter because we're not going to be accessing that. However, Wagtail does use that, so once you set it, you should probably not change it. And then we want to import our blocks. So our block was called title and text block

10:56

So before anything, uh we're actually going to need to import this as well. So from streams import blocks, and this is importing our version of blocks. py. Not Wagtail Core. This is our version. So this is from our streams. We can see it in here. And it's accessing blocks. py. Which is this. So now if we open up our terminal, everything should be working as expected. So let's flip over, and there it is. Everything is looking fine. Now we're going to run into a problem here. When we go to edit our page, it says no such column flexpage. content. Again, this is an SQL error. This is basically saying there is no column in our table called flexpage inside of our database called content.

11:44

And that's because in our models. py we have a content field here and we didn't actually run migrations. So let's do that now. We cancel that. We do python 3 manage. py make migrations and python 3 manage. py migrate. It is now asking us to either provide a one-off default for our content or to quit and let me add it in the models. py. So I'm gonna actually quit, and that is because we are gonna want more control over this. Now because this page already exists, we don't want to add anything special to it, especially because stream fields are a complex data type. So what I'm going to do is I'm going to say null is equal to true. And also blank is equal to true, so that all of these stream fields are completely optional.

12:32

Now I save that, open up my terminal, and let's rerun that command. Everything's looking good. Python 3, manage. py, run server, and let's go and open up our site. So now we have a section in here called content and you can see it's indicated with this little plus sign and we only have actually if I reload the page We only have this one little section here. Now if we add more stream fields, there will be more options in here. So our first stream field is title and text. So this is a custom title. In fact, let's do something a little more specific to, I guess, our website

13:21

Welcome to Startup Life. And because I don't want to write a bunch of actual text Just bear with me, just grabbing some lorem ipsum. Hmm-mm-mm-hmm-mm you see nothing. Carry on, carry on. Publish. Now, when we go and view this page. It still shows nothing. Now this is the last part. So what we've already done, we've already created the stream field. Now we just need our template to basically loop through all the stream fields and include the stream field information. So at this point it's really just a template job So let's open up our flex page.

14:07

And you can see that we have a subtitle in here, so we can do this. div subtitle. We're just gonna separate this a little bit. And in here is where we're going to add our stream field. So it's basically no not basically, it is. It is just a simple for loop. So all we have to do is type 4, and then what do we want to loop through? Well we want to loop through all the blocks in page. content. Page. content being the content field here And block is just what we're going to name it for each loop iteration. And simply going to include block, call it block, and lastly.

14:56

We need to load up Wagtail Core tags. Now to explain this a little bit Wagtelcore tags is a template tag file that we're loading and that file allows us to run a function called include block. Now that include block is going to loop through every single stream field that we have in our page, which is Just this one. So it's going to loop through the one block. It's going to check for that block. It's going to say, oh, where do I get that template file from? It's going to say, oh, okay, I get the template file from streams slash titleandtext block. html. Oh and just a quick note here, uh that template file is being explicit, so we're telling it where to get the template file from.

15:44

But I also mentioned that icon and label. And uh just before I forget, we can add another stream field in here, the exact same one if we wanted to. And because we only have the one, this is gonna be a little difficult to show. So I'm gonna actually get rid of that. Re-edit the page. And that is our icon and that is our label. So you can change those as well to anything you want. Alright, so if we go to our page and we refresh, it's going to say exactly what we expected it to say. The template does not exist, and it's looking all over the place for this thing. So let's go ahead and create a new template called titleandtextblock. html. So we go into our templates. So let's close up some of this stuff.

16:32

And let's create a new file or a new directory first. So this one's going to be called streams and then title and textblock. html Now, if we put anything in here, for example, put anything in here, and we refresh our page, the template error goes away and it says put anything in here. Now that will loop through every single time we have. one of our stream fields or in this case this particular stream field. For example, second title, second text. Let's add another one. Third title, third text. And let's say actually we wanted to move one of those up. So let's move that up so it goes the first one and then third title and then second title. Yes, it's weird ordering, but this will really

17:19

emphasize the point of stream fields. So let's save that and refresh our page. And it just says put anything in here, put anything in here, put anything in here. Not useful, but the loop is working. We have three stream fields that are being used and this is executing three times. So now at this point I'm not going to work on making this really pretty. I'm actually going to do that outside of this video. And you can adjust your style the way you want it to be. For me, I just don't think it's valuable to have you watch me write a bunch of HTML and CSS. So because this house this is our dedicated stream field, We want to put a title in here, so let's put I don't know maybe an H2. And we're going to put self. title. But Caleb, where did you get self. title from?

18:04

Well, self is coming from this class. Because it's a class, it is object-oriented, it is saying I need self, and then we get the property called title. And then in a paragraph, we can put self. txt. And just so that these are very distinct, let's put a horizontal rule in there and reload our page Hello, look at that. Welcome to Startup Life third title. Remember that was in second place because we swapped second and third. And if I go and edit that page again and let's Let's put the long one at the very bottom here. So let's just move that down. And let's put number two up top. So it goes number two, number three, and then the main one.

18:50

I publish that page. Number two, number three, and the main one. And that is the power behind stream fields. That is essentially we can take an entire section of content, i. e. this stream field, and we can move it anywhere else on the page as long as it's within our loop. So your loop is where your content is going to be. So you can have a banner before the loop, you can have a footer after the loop. But inside of the loop is where all of your stream fields are going to be moved around, and they can move anywhere within that loop Now if I lost you there, essentially what I'm saying is because this is a loop, all it's saying is you can rearrange these in any way you want.

Questions this talk answers

What is a Wagtail StreamField, and why use one?

A StreamField lets editors arrange sections of content in different orders instead of placing each section in a fixed location. This makes it useful for flexible page layouts.

Discussed at 0:00

How do I add a basic StreamField to a Wagtail page model?

Add a `StreamField` to the page model, expose it with `StreamFieldPanel`, import the necessary Wagtail classes, and supply the block definitions as the field’s list of block types.

Discussed at 3:53

How do I create a custom Wagtail StreamField block with a title and text?

Define a `StructBlock` in a `blocks.py` file, add required `CharBlock` and `TextBlock` fields for the title and text, and configure its template, icon, and label in the block’s `Meta` class.

Discussed at 6:19

How do I render StreamField content in a Wagtail template?

Load Wagtail’s core template tags, loop over `page.content`, and use `{% include_block block %}` for each item. Wagtail then selects the template configured for that block.

Discussed at 13:21

How can editors reorder StreamField content on a Wagtail page?

Put the StreamField blocks inside a template loop; editors can then move the blocks into any order within that loop while fixed content such as a banner or footer can remain outside it.

Discussed at 18:50

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