Django Backend Support for Hotwire

Hi folks,

first off, awesome work on Hotwire, of course.
I wonder if there is already some community or interested parties in reasoning about good integration of Hotwire into Django.
We closely followed the Hey development and all technical articles around it as we really like the general approach but went with Django applications due to better knowledge of python in our team. Thus, we would be really interested in a community effort around Django tooling for “native” Hotwire support in Django (similar to the raisl gems like turbo-rails).

WDYT?

Best
Julian

5 Likes

I’m experimenting with it at the moment for one of my side projects:

See also:

Works pretty much ok. A major problem is really the poor organization of the docs, to be expected for a beta I guess, and the lack of info and examples on how to set up with non-Rails environments (they just assume you use their gem with Rails I suppose).

The biggest hiccup right now is with form submission: this is due to the new behavior of Turbo which hijacks form submit and doesn’t allow re-rendering of the original HTML when validation fails. Again this behavior is undocumented and this might be a beta bug.

5 Likes

@JulianFeinauer I have a Django project (https://sharedgoals.co) that is running Turbolinks and Stimulus (1.4) and I’m just reading the docs for the new releases. Will let you know how I get on with the new stuff.

1 Like

Thanks both of you, @danjac and @rupertbaker! I will also start a little example with django but I think it would be worth the consideration to gather if there is nterest from multiple parties and do better code together over several half-maintained private side project :slight_smile:

Looks quite impressive your example. I will start to play a little bit with custom template tags (similar to their rails integration) where one woudl handle most of the heavy-lifting. I’ll keep you updated if / how it works.

Re form validation: I’ve played around a bit with turbo streams and think this might be a starting point.

This is a subclass of TemplateResponse: obviously you can do something similar with a TemplateView or whatever. The main thing is to set the correct content type.

class TurboStreamResponse(TemplateResponse):
    def __init__(self, request, template_name, context, *, action, target, **kwargs):

        super().__init__(
            request,
            template_name,
            context,
            content_type="text/html; turbo-stream;",
            **kwargs
        )
        self.context_data.update(
            {
                "turbo_stream_target": target,
                "turbo_stream_action": action,
                "is_turbo_stream": True,
            }
        )

This would be used in a Django view like this (I prefer FBV but this could be easily adapted into a mixin for CBVs):

@login_required
def user_preferences(request):

    if request.method == "POST":
        form = UserPreferencesForm(request.POST, instance=request.user)
        if form.is_valid():
            form.save()
            messages.success(request, "Your preferences have been saved")
            return redirect(request.path)
        return TurboStreamResponse(
            request,
            "account/_preferences.html",
            {"form": form},
            action="replace",
            target="prefs-form",
        )

    form = UserPreferencesForm(instance=request.user)
    return TemplateResponse(
        request,
        "account/preferences.html",
        {"form": form},
    )

So: render the full template in initial render, and a partial template if form validation fails, with a redirect on success.

Our full template looks like this:

{% extends "base.html" %}
{% block content %}
<div id="prefs-form">
  {% include "account/_preferences.html" %}
</div>
{% endblock %}

and our partial template:

{% load i18n widget_tweaks %}
{% if is_turbo_stream %}
<turbo-stream action="{{ turbo_stream_action }}"
              target="{{ turbo_stream_target }}">
  <template>
    {% endif %}
 
<form>
... rest of form lives here....
</form>
...

The important thing here is to set the correct tag and attibutes, and wrap in a tag if rendered as a stream. It would be easy enough to make a simple template tag to wrap the content.

There’s still an issue with forms from 3rd party packages and the like where it’s not so easy to rewire existing views (for example I usually just go with allauth for login/signup): unfortunately the current beta verson of Drive does not allow data-turbo=“false” on forms, which would allow you to ignore form submissions in these edge cases. That fix should be merged soon however.

2 Likes

I’m also really interested in helping write some wrappers/helpers to integrate Django and Hotwire.
It also looks like we are not alone by judging the interest on Twitter:

https://twitter.com/haruanmj/status/1341725331158396929
https://twitter.com/cndreisbach/status/1341488664652738561
https://twitter.com/n4Cr/status/1341465955013193730
https://twitter.com/avinashjoshi/status/1341476376289959939
hyyps://twitter.com/uninen/status/1341472744458780672

(Those are not links as the forum refuses posts with >2 links for new users)

Edit: I made a GitHub organization: @hotwire-django so we can centralize our efforts and code. (Still an empty shell at the moment) Ping me with your GitHub username and i’ll give you management permissions :slight_smile:

5 Likes

It would be interesting to get a POC up and running with Django channels and turbo streams.

2 Likes

That’s nice to hear. I currently do the setup for a simply package with just template tags for frames and all that. So that users don’t need to write the html tags but can go with custom template tags to do frames (and ideally also strrams afterwards). Lovely discussion here :heart_eyes:

3 Likes

Nice to see django people interested in hotwire! If you’ve heard of StimulusReflex which has similarities with hotwire, there’s an implementation of that in django called sockpuppet.

There’s also a discord where a lot of discussions happen around hotwire / reactive rails and django / sockpuppet / StimulusReflex. Feel free to join there!

7 Likes

I also come from the StimulusReflex Discord and have been exploring Stimulus and Django with the sockpuppet integration.

I noticed the django-stimulus and django-turbo repos, and I think it would make sense to explore whether SR and Hotwire have different needs. I strongly suspect that they don’t, and if so it might make sense for both potential communities to collaborate to standadize the process of adding Stimulus/Turbo to your project regardless of what you use to talk with the server. It makes no sense to maintain the functionality in two different places, since it’s entirely possible to want to use Stimulus with Django without anything else, and people with that goal should have one app they can rely on.

1 Like

I’m no Python dev :slight_smile: , but I spiked out an experimental Flask/Turbo integration last night for fun: https://github.com/cdmwebs/cairo. The main thing seems to be the event-stream from the server side.

I got how to use it with forms, works fantastically!

@Joirichi on github. Please give me management permissions ASAP, i just discovered something with djangoforms and hotwire.

Genius! Works great!

Could you share some code?

1 Like

I found this morning an interesting post with a working demo of a chat app with Django Channels integrated with Turbo Streams. The author also seems to plan packaging the code into an app in the future. I’ll try to contact them so we can join our efforts.


Side note: @Medicalcoder and @avinash, i added you to the GitHub organization. If someone else wants to be added too, ping me :wink:

5 Likes

Hi, If possible can you add me too.

Hi there! Author of that article that @0xc4 posted. Definitely would be interested in joining the efforts here. I just added a README to the demo repository with instructions on how to get it up and running. The turbo app within the demo repo is already pretty self-contained, and has everything you need to get started with the BroadcastableMixin for websockets and Turbo Streams. Feel free to ping me here if you have any questions!

2 Likes

Very cool davish. I’m putting a priority on family today, but if I have a moment I’ll see if I can dig into your example, I think it’s going to be very helpful.

@0xc4: feel free to add me to the organization.