Ephes Blog

Miscellaneous things. Not sure what to put here, yet.

TIL: Validating Wagtail Page Fields Only Upon Publishing

, Jochen

Recently, I encountered an issue when publishing a podcast episode about LLMs using django-cast. The episode failed to appear in the podcast feed and the announcement tweet lacked the newly added Twitter card. The cause? I forgot to include the podcast_audio field.

Ideally, the system should have displayed an error when attempting to publish an episode without the podcast_audio field. However, we still want to allow draft episodes to be saved without the audio field.

Initial Solution Suggested by ChatGPT

For this LLM-focused episode, I initially sought guidance from ChatGPT. It provided a seemingly promising solution that appeared effective upon first inspection:

class MyCustomPage(Page):
    field1 = ...
    field2 = ...

def clean(self):

    # Check if the page is being published
    if self.status == 'live':
        # Perform custom validation for required fields
        if not self.field1:
            raise ValidationError({'field1': "Field1 is required for publishing."})
        if not self.field2:
            raise ValidationError({'field2': "Field2 is required for publishing."})

Upon closer examination, the correct condition should have been if self.status_string == "live". However, the proposed solution still didn’t function as intended. Instead of displaying a user-friendly error message in the Wagtail admin interface, attempting to publish an episode without the podcast_audio field resulted in an unwieldy traceback, which would have led to an internal server error in production.

Revised Solution

The previous solution didn't work, because Wagtail only displays a nice error message when the ValidationError is raised while it tries to save a draft revision. Therefore we need to raise the ValidationError during the first call of the clean method where the status_string is still "draft". But how do we know the page is about to be published then?

After tinkering with the before_edit_page hook for a while, I finally landed on this solution:

class CustomEpisodeForm(WagtailAdminPageForm):
    Custom form for Episode to validate the podcast_audio field.

    The reason for this is that the podcast_audio field is not required
    for draft episodes, but it is required for published episodes. So
    we have to check which button was clicked in the admin form.

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["action-publish"] = forms.CharField(required=False, widget=forms.HiddenInput())

    def clean(self) -> dict[str, Any]:
        cleaned_data = super().clean()
        if cleaned_data.get("action-publish") and cleaned_data.get("podcast_audio") is None:
            raise forms.ValidationError({"podcast_audio": _("An episode must have an audio file to be published.")})
        return cleaned_data

class Episode(Post):
    base_form_class = CustomEpisodeForm

This CustomEpisodeForm class checks whether the “Save draft” or “Publish” button was pressed in the Wagtail admin and stores the information for use in the clean method. This solution successfully validates the podcast_audio field only upon publishing the episode, while still allowing draft episodes to be saved without the audio field.

Weeknotes 2023-04-10

, Jochen
"for legal reasons this is a joke" --julia, serial project starter

Lots of colored eggs. Short work week where I was collecting some material for an upcoming pytest-course and did some research about this whole shiny new LLM field which is completely wild atm. Recorded a podcast episode about LLMs. Released a new django-cast version with some minor improvements. Fixed a 500 error on this site caused by the csrf-middleware raising an error and context-processors weren't run so the custom error template didn't get its base template (this needs more fixing).


Mastodon / Twitter






Weeknotes 2023-04-03

, Jochen
if my editor reads this: everything’s totally fine --Ethan Marcotte

Not much work, but a fair share of paperwork, and finally traveled to Erlangen. New django-cast-release and applied with django-cast to the prototype fund🤞. Downloaded the LLaMA weights, and hope to find some time to play around with LLMs locally next week.



Mastodon / Twitter

Open Source Funding



Full-Stack Team Now Open for Projects: A First-Time Announcement! 🚀

, Jochen

Hello everyone!

It's been over ten years of self-employment, and I've never publicly announced my availability for new projects—until now! 😀 I'm not just looking for opportunities for myself, but for a small full-stack team (design, frontend, and backend) as well. We're seeking projects that will last at least a few months.

To get in touch with us, please use one of the methods listed on my contact site.

We're excited to bring our expertise to your projects and collaborate with you!



Weeknotes 2023-03-27

, Jochen
we saved daylight but at what cost --K

Much less work last week, but I still had a lot of paperwork to do. Worked on htmx integration for django-cast during this year's Python Spring Sprint & Hackathon in Düsseldorf. Htmx is now working for pagination in both built-in themes. See the latest release notes for all django-cast-related updates. Had a lot of fun attending the latest Django Meetup Cologne.

Despite being self-employed for over ten years now, I never publicly announced being open to new projects. Until now 😁!





Mastodon / Twitter