Choosers - Matthew Westcott

This video features Matthew Westcott at Wagtail Space NL 2022 in Arnhem, Netherlands.

Choosers - Matthew Westcott
0:14:38
Published June 30, 2022
240 views

Summary

Wagtail’s page, image, document, and snippet choosers had duplicated code and complicated internals that made custom choosers fragile to maintain. Matthew Westcott describes consolidating them into a reusable framework where developers can define a chooser with a small amount of model-specific configuration, while Wagtail handles the widget, modal workflow, JavaScript behavior, and links to edit selected objects. The broader aim is to turn established Wagtail interface patterns into reusable building blocks, so extensions can feel native without copying large chunks of Wagtail code.

Key takeaways

  • A chooser involves both a form widget and a modal workflow, with search, pagination, uploads, validation, and other interactions to manage.
  • A handler factory lets conventional choosers use default JavaScript behavior without requiring developers to write large custom handler dictionaries.
  • Viewsets reduce boilerplate by grouping related views and their shared configuration; the same idea supports concise chooser definitions.
  • Registering a chooser lets Wagtail connect it to model fields automatically, so developers can use ordinary field panels.
  • The refactor advances Wagtail’s broader effort to expose mature interface patterns as reusable framework components instead of relying on copied internal code.

Summarised automatically from the transcript.

Transcript

2,563 words · auto-generated Show

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

0:02

Okay, yep, so hello, I'm Matt, uh otherwise known as Gasman, if you uh uh have sort of spent any time I'm on the uh GitHub or uh Stack Overflow. And right now I'm doing a lot of work rewriting choosers. And the aim of this work is firstly to bring together all of the chooser implementations in Wagtail. the page chooser, the image chooser, document chooser, snippet chooser into a single implementation rather than copied and pasted code sort of all over the place. And secondly, to make that one unified implementation available for easily building choosers of your own for arbitrary models without having to do that copy Copying and pasting of huge chunks of Wagtail code. So that might be things like video choosers for a package like Wagtail Media, or if you're doing an e

0:51

-commerce integration with something like Oscar, a product choosers. And I've picked this out as a topic because I think it captures the nature of a lot of the work I do on Wagtail, getting stuck into the low-level code changes and refactoring while also hopefully having an impact on the big picture strategic direction of Wagtail as a product um as a project. So you might think what what is the big deal here? How complicated can choosers really be? Quite complicated as it turns out So I'll give a warning here. This talk is full of annoying details you don't care about. Or more exactly, it's full of annoying details that you shouldn't have to care about. about as a Wagtail site developer. During the sprints this week uh Storm was doing some work around choosers and he said to me, oh

1:38

I thought of choosers as this fairly simple boring part of Wagtail But there's a lot going on there, is it isn't there? And and that's uh very true. If we uh to look at a very simple case of the choosing pattern we'll go for to this uh person snippet here and when we load this page uh we have a representation of a person object uh we click a button to open that this list of choices choose one and then that replaces the item that was there before and if you're limiting things to that particular journey then yes it's pretty simple simple. There's uh various packages out there like uh Wagtail Model Chooser that copy and paste these bits of Wagtail code to replicate that package. pattern and of course copying and pasting

2:24

bits of internal Wagtail code like that always end up being a bit of a moving target and breaking in later Wagtail releases after we've changed them, but these packages fulfill a need. But even this simple case becomes complex when we look a little deeper because there are actually two different components of play here. There's the chooser widget, the thing that appears as part of the form, and the uh the chooser modal views. We have to make that distinction because uh you can have one without the other. If you're launching the if this from the rich text editor, you're using the chooser modal to pick an image. but you're not doing that from the choose a widget. It's a whole different widget that's

3:10

uh that's dealing with this. And so on my first attempt at creating this single unified code base for the chooser pattern, which is the Wagtail generic chooser package. This documentation starts out by saying, okay, there are two different components that you need to know about here. There's the the widget and the chooser chooser. So already we have this internal complexity leaking out into the end user world. It's like if you want to use this Here's the first thing you need to know. And I really think we should be able to do a a lot better than that. Think of it like a swan, kind of graceful above the surface. even if down below it's paddling light mad to do its thing. So yeah, let's dig a bit deeper again.

3:57

So we we open up this modal window, hand over control to it, and at some point in the future we'll get a result back from it that we can then put into the the choose a widget but while this window is open there's a lot that can happen Here we've got of the filtering and searching that will sort of display new uh a new set of results. There's pagination. Uh we can switch over to this tab and upload a new image. And uh in in the case of uh Yeah, well this this form it can fail validation and have to reshow just that form while keeping the rest of the modal intact. And as of Wagtail 3, if you do successfully upload an image, then it might recognize that that's a duplicate and present you with a whole other confirmation

4:43

step so there's a a lot of stuff that has to go on here while the uh while while and just in this window without leaving the the original original form that you are on. All of these things involve a back and forth request to the server and we have a component within Wagtail to manage that called modal workflow. So if we were to to go through some of those interactions with the developers tools open, you'd see some responses, JSON responses like this. So it's running all of these requests in the background so that we're never leaving the page And you'll see that there is this step item in this JSON which

5:28

identifies kind of where in that sort of flowchart we are, whether so in this case this is step chooser so this is just rendering the initial chooser view. And each of these steps has a corresponding sort of chunk of JavaScript in this sort of dictionary here to say how we should sort of handle this particular particular step and it's a big old chunk of code. Right now every one of these every kind of chooser in in Wagtail has one of these dictionary It has to because they have all of these slightly different feature sets. And that's bad news for our goal of opening up this framework to allow people to create their own choosers. because we don't really want to say, okay, you want to build a chooser? Sure, just supply your own hundred

6:13

line chunk of JavaScript. As I say, we can do better than that. And the solution that I came up with was the delightfully named Chooser Modal Onload Handler Factory. Believe me, when I came up with that name, I had this crisis. Am I turning Wagtail into some enterprise Java application? But th this yeah, as complicated as this is, this is the the swan paddling under the surface to make every everything look graceful. And to put it simply, this is an object that creates that big old dictionary of JavaScript handlers. So this is the the new code for the document chooser. Which is very close to a plain vanilla chooser with no extra behavior. The only one bit of kind of special quirk it has is a

6:58

fairly obscure bit of usability. which is uh if you go to a collection that has no items in it and you get this link why not upload one now then that's uh that that will uh display the the chooser form with that collection pre-filled, the the the upload form with with that yeah pre-filled. So we've just got this one function that sets that up. So we've we've so now we we've gone down from sort of sort of hundreds of lines of code to just this this one special case. And if you're if you have a chooser that does everything in the totally conventional way, you don't need to customize this this at all. So there's no new JavaScript arrive.

7:44

And at that point, this is just an internal detail that you'll probably never need to worry about ever again after this talk. So another thing that has really helped to cut down the amount of boilerplate code in the these choosers is the concept of viewsets, which is something that we came up with a few years back. I think it's uh Carl that did the in initial implementation implementation for the simple crud views like the uh sites admin area. Um we happily embrace Django's class based views for this sort of thing so that we don't have to sort of write this sort of very very of standard code code to say here's a here is a site model, here is a form class, just give me an edit view that will deal with uh

8:29

editing it. And that's that's great. And so you've got sort of the edit view, the create view, the delete view, the index view, and it and Django supplies all of those. But it leaves you writing a lot of definitions to connect these up because here's the the edit view. You've got this. So when you submit it, it redirects back to the index view. It also has this button link it to the delete view and each of those cross-links is another line of code to tell it where to find those other views. And uh it's actually a lot more effective to define this group of views all in one go so you don't need to write all that spaghetti to connect them up. And at its simplest, this is what that can that code can look like. So this is just, yeah, give me a view set that manages a person

9:16

and has these form fields. So just this one bit of code will give you uh you an interface like this. So it's uh something that's kind of really useful to have as as a pattern in Whitetail. Going back to the chooser modal view it struck me that this is a very similar situation, a bunch of interlinked views with sort of shared configuration that you don't want to have to repeat for each view. So to cut to the chase, this is now what a chooser definition looks like at its absolute simplest. Well in fact the the absolute simplest would be if you just had model equals person and and ignored all of the other stuff, then you'll get sort of uh sort of the standard snippet icon and uh and just

10:02

and quite generic labels but um but yeah so so we we we've got the end to end chooser just uh with a couple of lines of code. And as we've seen choosers can get a lot more complicated than this with custom functionality. and all sorts of edge cases, but handling those should, when I'm done with this bit of development, just be a case of overriding methods on this one. class. So ultimately we'll be able to define the image chooser and the document chooser, all of these different choosers just as a sort of variance of this. And yeah, so this is how our resulting person chooser looks. And that's that tiny definition that we just saw is actually doing a really wide range of things across WAG.

10:48

I mentioned earlier that we distinguish between the chooser widget and the chooser modal. And uh well, unlike the old Wagtail generic chooser package , package this handles both of them in one go and because that that separation is a detail that you probably don't care about. You just want to say give me a chooser I know what a chooser is supposed to behave like in Wagtail, just give me that. And you don't have to dig into all of these internals. And those of you who've tried out Wagtail 3 will know that we no longer have to say use snippet chooser panel panel, image chooser panel, and so on. We we can just use a plain field panel because Wagtail now has built-in logic to say, okay, this field is a foreign key to image. I know what to do with this. I've got a widget type to handle that.

11:36

And this new chooser framework participates in this logic. So once you've registered this chooser view , set with just those that couple of lines of code this widget will just get used automatically so just um Yeah, just use use a field panel and it will know to use this uh this widget that you've just defined. Uh we haven't sort of talked about that edit this person button yet. Yeah, yeah. So when you think about it, there's a lot of possible places that that could point to, depending on whether that model is a snippet or something using model admin or something like images with its own dedicated area. area in Wagtail and knowing where to link to is actually a a a problem that that we've had to solve before uh

12:23

for the uh the site history reports that we introduced in to Where you want to be able to show these are the sort of objects that have been changed recently and you need to if If people have edited a person snippet, you need to be able to link there. So we've got already got this system where when you set up one of these uh uh crud views it will uh yeah you can declare I am the edit view for this particular model and um it's uh so that this is all uh all stuff that we can take take out advantage of here and I find this sort of thing really exciting. Wagtail is it's a pretty mature product now. We've found these user interface patterns that work well

13:08

and and uh we're now in this process of uh opening up Wagtail as a framework so that in your own code you can take advantage of those patterns In your own code. And so far it's been a bit of a slog, a bit of a slow process of taking these sort of recurring patterns and say , okay, how can we extract that as a reusable building block? I haven't even talked about the table component that we use for the listing things inside the chooser. So that you can, if you want sort of several columns in that, in the chooser, then that's another thing that you can just do with another line of definition. But yeah, as as I'm building this, I'm constantly finding that I can join it up to some other part of the framework that I worked on opening up a couple of months ago.

13:56

And it feels like things are really starting to snowball this this effort of opening things up is really really paying off and we're starting to sort of yeah be able to just build all of these things that behave in the understood wagtail way and just by plugging bits of code together and not having to just copy and paste huge chunks of code. And I think that's a really exciting direction for uh for Wagtail in the the coming months and years. So yeah, really excited by that. Thank you very much.

Questions this talk answers

What is Wagtail’s chooser refactor trying to achieve?

It brings page, image, document, and snippet choosers into one shared implementation, and makes that framework reusable for custom model choosers without copying Wagtail’s internal code.

Discussed at 0:02

How does the chooser modal handler factory reduce custom JavaScript?

It generates the modal’s JavaScript handlers for conventional chooser behavior, so a chooser usually needs no custom JavaScript; only unusual behavior requires a small override.

Discussed at 6:13

Why use viewsets for Wagtail chooser views?

A viewset groups related, interconnected views under shared configuration, reducing repetitive code for wiring up each view. The chooser framework uses the same idea to define a chooser compactly.

Discussed at 7:44

How can I create a custom chooser in Wagtail without copying chooser code?

Define a chooser viewset for the model; a minimal definition can provide the standard chooser behavior, with custom behavior added by overriding methods when needed.

Discussed at 9:16

Can a custom Wagtail chooser work with a regular FieldPanel?

Yes. Once the chooser viewset is registered, Wagtail can recognize the model field and select its chooser widget automatically, so you can use a plain FieldPanel.

Discussed at 10:48

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 Matthew Westcott

More videos from Wagtail Space NL