08 Jul 2026

feedDjango community aggregator: Community blog posts

A small proposal to form rendering in Django

It's been a while since my last post, mainly because June saw me start a new client, GSOC really taking off and we have our first real customers in Hamilton Rock with money being deposited and some money being spent, not without its teething issues! Also with a fair amount of social engagements as well!

But anyway, on to today's post. During June I proposed a new feature idea which is an extension to Django's form rendering capabilities to include widgets templates inside a form renderer. Currently, it's only possible to Override widgets at a project level by specifying the template name, or you have to overwrite the widget and then specify your own custom template name and then use that custom widget. It's not possible to customize widgets at the form renderer level.

My idea is to extend the form renderer API. Well actually extends the budget rendering API to check the specified form renderer. It should only be an extension to a private method inside the widget API. Below is the relevant code that I actually got Claude to spit out inside Hamilton Rock today. This is a first iteration which very likely needs some improvement, but it does work!

_CAMEL_BOUNDARY = re.compile(r"(?<!^)(?=[A-Z])")

class Widget(metaclass=MediaDefiningClass):
    ...
    
    def _render(self, template_name, context, renderer=None):
        if renderer is None:
            renderer = get_default_renderer()
        # Walk the widget MRO for a ``<widget>_template_name`` on the renderer.
        # A class that defines its own ``template_name`` short-circuits (attribute
        # shadowing): a custom widget keeps its template over a base override,
        # while an unstyled subclass resolves up to a styled base.
        for klass in type(self).__mro__:
            slug = _CAMEL_BOUNDARY.sub("_", klass.__name__).lower()
            override = getattr(renderer, f"{slug}_template_name", None)
            if override is not None:
                template_name = override
                break
            if "template_name" in klass.__dict__:
                break
        # Same trust posture as Django's own Widget._render.
        return mark_safe(renderer.render(template_name, context))  # noqa: S308

and here is the current method from the source

    def _render(self, template_name, context, renderer=None):
        if renderer is None:
            renderer = get_default_renderer()
        return mark_safe(renderer.render(template_name, context))

There is also some code to allow admin classes to specify a renderer so that your custom renderer doesn't overwrite admin form widgets. In the coming week or so, I will extract this code into a third-party package for others to use.

But what's the real win with this potential change? Honestly I see this unlocking simple packages which unlock custom and complete form rendering packages with Django. Most of these themes would be HTML, CSS & Javascript, with the only python being the declaration of the FormRenderer class like so (pulled from Hamilton Rock):

class DrawerFormRenderer(TemplatesSetting):
    form_template_name = "forms/drawer_form.html#form"
    field_template_name = "forms/drawer_form.html#field"

    text_input_template_name = "forms/drawer_form.html#text_input"
    email_input_template_name = "forms/drawer_form.html#text_input"
    password_input_template_name = "forms/drawer_form.html#text_input"
    date_input_template_name = "forms/drawer_form.html#text_input"
    number_input_template_name = "forms/drawer_form.html#number_input"
    select_template_name = "forms/drawer_form.html#select"
    textarea_template_name = "forms/drawer_form.html#textarea"
    checkbox_input_template_name = "forms/drawer_form.html#checkbox"
    radio_select_template_name = "forms/drawer_form.html#radio"

If you like the look of this, give the feature a thumbs up on the issue and we can hopefully get it progressed. Also do let me know what glaring holes that I have missed in this idea.

08 Jul 2026 5:00am GMT

03 Jul 2026

feedDjango community aggregator: Community blog posts

Issue 344: Happy Birthday Djangonaut Space!

03 Jul 2026 3:00pm GMT

02 Jul 2026

feedDjango community aggregator: Community blog posts

Python Leiden (NL) meetup summaries

Two summaries of the July 2 2026 Python meetup in Leiden. I've omitted one, "Python with Karel" by EiEi Tun, as I've made a summary of that talk in Utrecht a month ago, already :-)

Building modern internal team CLIs with incremental automation - Farid Nouri Neshat

Obligatory xkcd cartoons: https://xkcd.com/974 and https://xkcd.com/1319 and https://xkcd.com/1205

Toil: manual, repetitive, automatable, distracting you from your real work, no enduring value. Yes, he likes to automate things :-) Some examples of repetitive manual tasks:

  • Creating dev containers.
  • Gathering data for troubleshooting.
  • Something that needs to be set manually in a database.
  • Setting up a new AWS account.
  • Creating a new dev environment on the new colleague's laptop.

How to automate? Do it iteratively! Your boss might not like you to spend a day automating the task. But if you do it small steps at a time...

  • Do it manually the very first time.

  • Then start with documenting the steps.

  • Then turn it into a do-nothing scaffold script:

    def step1():
        print("Open the AWS page manually")
        input("Press enter to continue")
    
  • Everytime you do the task, automate a small bit and flesh out the script over time.

  • After many iterations, you'll have automated it fully!

"I don't have time to automate it", you might say? Well, why don't you have time? Is it perhaps because you haven't automated things?

A good motivator: if you hate the task... Hate driven development :-)

After a while, you'll have lots of random scripts. Stuff them in a repository. Slowly document them. Try to get them to use the same conventions. Perhaps you can re-use functionality in a library.

Something you need quicky is some CLI, a command line interface. He likes typer to make his CLIs: much nicer than Python's own "argparse":

import typer

app = typer.Typer()


@app.command()
def hello(name: str):
    print(f"Hello {name}")


if __name__ == "__main__":
    app()

AI comment: AI agents can use your CLI. Use the docstring and help functions to help orient the AI to your custom CLI. You can, for instance, use a CLI to give the agent access to your database's content without giving it direct access to the database.

AI agents can be dangerous. A solution might be to use "feature flags". You can disable production access until you enable some setting or flag that AI doesn't know about.

He also mentioned the rich library for formatting and colorizing your textual output.

What I've learned maintaining the MCP Python SDK - Marcelo Trylesinski

He's one of the three maintainers of the MCP Python SDK. SDK = software development kit. MCP: model context protocol, so a way for AI agents to connect to some other piece of software.

MCP is basically "OpenAPI for your agents". It exposes three things from the server side:

  • tools
  • resources
  • prompts (though tools are mostly the only thing that is used)

The client provides:

  • sampling
  • elicitation (="producing a reaction", so mostly it means that the AI server asks you questions)
  • roots
  • logging

The MCP spec kept growing. But clients never caught up, so it was mostly only the "tools" part that got used.

A big problem is that servers cannot scale. The AI server might have lots of machines with a loadbalancer in front of it, but as a user you need to stay connected to the one machine that has your context.

There's a new version of the spec (final version this month) that actually removed stuff, instead of growing. The "client provides" list mentioned above? Sampling, roots and logging are gone as they were hardly used.

MCP is now a small core, with optional extensions. Examples: tasks, MCP apps, enterprise auth.

The MCP Python SDK supports the new version, too. He demonstrated a small Python script that had a function that said you could have three bananas. He connected it via MCP to Claude and could ask Claude for the number of available bananas. It got back, via the Python tool, with the correct answer.

02 Jul 2026 4:00am GMT