An early stage integration of Hotwire Turbo with Django

Overview

Build Status Documentation Status Issues Twitter

Note: This is not ready for production. APIs likely to change dramatically. Please drop by our Slack channel to discuss!

Turbo for Django

Requirements

  • Python 3.6+
  • Django 3.1+
  • Channels 3.0+ (Optional for Turbo Stream support)

This repository aims to help you integrate Hotwire Turbo with Django. Inspiration taken from @hotwired/turbo-rails. Documentation can be found here for the current integration.

Discussions about a Django/Hotwire integration are happening on the Hotwire forum. And on Slack, which you can join by clicking here!

As we discover this new magic, you can expect to see a few repositories with experiments and demos appear in @hotwire-django. If you too are experimenting, we encourage you to ask a write access to the GitHub organization and to publish your work in a @hotwire-django repository.

We expect to gain knowledge and experience with Hotwire over time and will try to extract useful code from the demos and package it in self contained "pip-installable" packages: turbo-django and stimulus-django.

Structure

The turbo directory contains the package with helpers, templatetags and utilities for integrating Turbo tightly into Django. Currently, it contains a Broadcastable mixin and a Django Channels websocket consumer to allow for realtime updates with Turbo Streams.

License

Turbo-Django is released under the MIT License to keep compatibility with the Hotwire project.

If you submit a pull request. Remember to add yourself to CONTRIBUTORS.md!

Comments
  • How to decide what to implement?

    How to decide what to implement?

    I'm wondering what a good way to move forward is. Should we add utilities as people's projects seem to need them? Should we look at what's offered in hotwired/turbo-rails right now and port it over to Python? The second seems to make sense to me, but I personally don't have any experience with Ruby and have had trouble in the past reading through Rails codebases. If anyone else has experience in that area then I'd love to work with them!

    discussion 
    opened by davish 10
  • Fresh install issues

    Fresh install issues

    Hi Everyone!

    Been figuring out if I should go with Hotwire or htmx/alpine and wanted to get something running to then explore how things work as I'm pretty new to coding so abstractions are a bit tricky.

    Followed the instructions to get the chat experiment running and it runs, and unfortunately I've run into a ValueError: `[18/Nov/2022 18:53:13] "GET /ws/ HTTP/1.1" 500 131700 Internal Server Error: /ws/ Traceback (most recent call last): File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\fields_init_.py", line 1823, in get_prep_value return int(value) ValueError: invalid literal for int() with base 10: 'ws'

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last): File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner response = get_response(request) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\base.py", line 70, in view return self.dispatch(request, *args, **kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\base.py", line 98, in dispatch return handler(request, *args, **kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\detail.py", line 106, in get self.object = self.get_object() File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\detail.py", line 36, in get_object queryset = queryset.filter(pk=pk) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 941, in filter return self._filter_or_exclude(False, args, kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 961, in _filter_or_exclude clone._filter_or_exclude_inplace(negate, args, kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 968, in _filter_or_exclude_inplace self._query.add_q(Q(*args, **kwargs)) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1416, in add_q clause, _ = self.add_q(q_object, self.used_aliases) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1435, in add_q child_clause, needed_inner = self.build_filter( File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1370, in build_filter condition = self.build_lookup(lookups, col, value) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1216, in build_lookup lookup = lookup_class(lhs, rhs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\lookups.py", line 25, in init self.rhs = self.get_prep_lookup() File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\lookups.py", line 77, in get_prep_lookup return self.lhs.output_field.get_prep_value(self.rhs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\fields_init.py", line 1825, in get_prep_value raise e.class( ValueError: Field 'id' expected a number but got 'ws'. [18/Nov/2022 18:53:24] "GET /ws/ HTTP/1.1" 500 131700 Internal Server Error: /ws/ Traceback (most recent call last): File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\fields_init.py", line 1823, in get_prep_value return int(value) ValueError: invalid literal for int() with base 10: 'ws'

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last): File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner response = get_response(request) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\base.py", line 70, in view return self.dispatch(request, *args, **kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\base.py", line 98, in dispatch return handler(request, *args, **kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\detail.py", line 106, in get self.object = self.get_object() File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\views\generic\detail.py", line 36, in get_object queryset = queryset.filter(pk=pk) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 941, in filter return self._filter_or_exclude(False, args, kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 961, in _filter_or_exclude clone._filter_or_exclude_inplace(negate, args, kwargs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\query.py", line 968, in _filter_or_exclude_inplace self._query.add_q(Q(*args, **kwargs)) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1416, in add_q clause, _ = self._add_q(q_object, self.used_aliases) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1435, in add_q child_clause, needed_inner = self.build_filter( File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1370, in build_filter condition = self.build_lookup(lookups, col, value) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\sql\query.py", line 1216, in build_lookup lookup = lookup_class(lhs, rhs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\lookups.py", line 25, in init self.rhs = self.get_prep_lookup() File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\lookups.py", line 77, in get_prep_lookup return self.lhs.output_field.get_prep_value(self.rhs) File "C:\Users\rob_s\PycharmProjects\Autobiography\venv\lib\site-packages\django\db\models\fields_init.py", line 1825, in get_prep_value raise e.class( ValueError: Field 'id' expected a number but got 'ws'. [18/Nov/2022 18:53:35] "GET /ws/ HTTP/1.1" 500 131700 `

    I've tried searching the code base for ws to see where it was coming from but no luck, any pointers with this?

    Also, I'm not sure if this is related to this issue, or if I haven't understood what the demo was supposed to show, but when I submit a new message in the chat, it requires a full page reload.

    My (perhaps mis)understanding was that turbo enables real time DOM manipulation without reloading, is this correct or have I got the wrong idea?

    opened by RobSisson 7
  • component subscription calls without

    component subscription calls without "user"

    The AlertBroadcastComponent example works well, but when I change to the UserBroadcastComponent, the example starts as normal, the webpage renders, but after a few seconds, I get an error:

    User `None` does not have permission to access stream 'prescriptions:AlertBroadcastComponent'.
    

    I tried to dig into that, but could not find the culprit... But I think this is a bug.

    opened by nerdoc 7
  • Refactoring to unlink Streams from Models

    Refactoring to unlink Streams from Models

    Solves #14

    This is not yet fully polished but I wanted to hear Feedback from others like @davish and @blopker about these changes.

    Basically, I subclassed BroadcastableMixin with a BroadcastableModelMixin which contains the Model spezific logic and migrated all model-specific tasks from Consumers notify method there. So now we should be able to send general Messages with the consumer.

    As Demo I added the wiretap view ("/wiretap") into the Chat Demo which receives all Messages send in all Channels.

    What do you think?

    opened by JulianFeinauer 7
  • Quickstart not working

    Quickstart not working

    High level: following Quickstart did not result in a working example. With 1 shell running Django web server and one running a Django shell, I was not able to produce expected functionality. It seems that channel broadcasts were lost in the ether. This could be a missing settings, but then again, I would expect to see an error if that were the case.

    Info:

    // requirements.txt
    django==3.1.13
    uvicorn[standard]==0.15.0
    turbo-django==0.2.4
    websockets==10.0
    channels==3.0.4
    ....
    

    OS: OSx 10.15.7 (Catalina)

    Please let me know how I can assist.

    opened by elamje 5
  • A few stream consumer refactors

    A few stream consumer refactors

    I noticed that when navigating to different pages with a stream open, we'd get multiple stream responses like the unsubscribe event wasn't working. This is an attempt to fix that.

    opened by blopker 4
  • Vendor JS Libs

    Vendor JS Libs

    This PR vendors the JS libraries. This is useful so users of this package don't have to rely on third party CDNs which is against many companies' security policies and adds another failure point to sites. If people want to use other versions of the vendored libs or want to create their own JS bundle, it should be easy enough to just not include the head.html template and use their own solution.

    List of changes:

    • Changed chat app setup instructions to use Python 3's built in venv module so we don't rely on the virtualvenv package being installed globally.
    • Added some seed data to the chat app so people can get set up easier.
    • Removed channels from install_requires so people using WSGI don't need to have it installed. We'll want to mention something about installing it in the Streams install instructions.
    • Moved the previously inline JS into its own JS file, then wrapped it in IIFE so variables don't leak. We can minify this at some point, but it's so small now it didn't seem worth it.
    • Changed turbo.js to load the es2017 version as our custom JS wont work on es5 anyway.
    • Changed reconnecting-websocket to the IIFE version minified which is smaller and avoids an exception with the CJS version.
    • Used the static tag with data-turbo-track="reload" so the JS files get fingerprinted and Turbo reloads itself when those resources change in production.
    • Added defer to script tags so they don't block rendering the rest of the page.
    • Dynamically create the websocket URL to work in other environments besides localhost, also now supports secure connections.
    opened by blopker 4
  • render() requries context (Components require documentation)

    render() requries context (Components require documentation)

    In #53 (I didn't find another documentation of Components) you write at the end: cart_component.render() - but the render function has a mandatory context parameter.

    So in my tests I could not get this to work...

    Is there any documentation for Components (which are GREAT!)?

    opened by nerdoc 2
  • "nested" Streams app name breaks turbo?

    I am evaluating turbo-django for a project, am struggling myself throught the tutorial and creating some of the ideas in my project.

    I added a (empty) Stream class named PrescriptionRequestStream, and included {% turbo_subscribe 'prescriptions:PrescriptionRequestStream' %} into my template.

    Now Django complains:

    TemplateSyntaxError at /dashboard/requests
    
    Could not fetch stream with name: 'prescriptions:PrescriptionRequestStream'  
    Registered streams: ['medux_online.plugins.prescriptions:PrescriptionRequestStream']
    

    It's a very brilliant idea to list the available streams in the error message, cool to debug. BUT: I copy'n'pasted medux_online.plugins.prescriptions:PrescriptionRequestStream into the templatetag. And I get the same error for that:

    Could not fetch stream with name: 'medux_online.plugins.prescriptions:PrescriptionRequestStream'  
    Registered streams: ['medux_online.plugins.prescriptions:PrescriptionRequestStream']
    

    According to stream_for_stream_name(), there is a hint that it should be >>> stream_for_stream_name("app.RegularStream") - meaning the namespace before the class is the app, and it is not a dotted path.

    But then the app name generation in Turbo is done wrong when apps are not in the first level of Django's directory tree.

    The problem seems to be in the autodiscover_streams() method: app_name = broadcast_module.__package__ uses a dotted path as the app name.

    I replaced it with app_name = broadcast_module.__package__.split(".")[-1]and it worked instantly.

    This may not be the best approach, as it's just a first glance into the code of turbo-django - maybe you have a better idea and the bigger picture.

    opened by nerdoc 2
  • Improve Quickstart by adding requisite steps

    Improve Quickstart by adding requisite steps

    When running the example I ran into several configuration errors due to not understanding that I must read Tutorial: Part 1 first. It does not specify to go through the tutorial set up first, leaving several settings missing.

    I believe Quickstart can be improved by adding two steps.

    1. pip install django turbo-django channels
    2. Add turbo and channels to INSTALLED_APPS. Change WSGI_APPLICATION = 'turbotutorial.wsgi.application' to ASGI_APPLICATION = 'turbotutorial.asgi.application'
    ASGI_APPLICATION = 'turbotutorial.asgi.application'
    
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'turbo',
        'channels',
    ]
    
    CHANNEL_LAYERS = {
        "default": {
            # Don't use this backend in production
            # See https://channels.readthedocs.io/en/latest/topics/channel_layers.html
            "BACKEND": "channels.layers.InMemoryChannelLayer"
        }
    }
    
    opened by elamje 2
  • UUID as a primary key

    UUID as a primary key

    Hey, it might be something that can be easily solved, but my initial approach to turbo-django is stoped by "Object of type UUID is not JSON serializable" error.

    Error: site-packages/turbo/templates/turbo/turbo_stream_source.html, error at line 2 <turbo-channels-stream-source signed-channel-name="**{{ stream.signed_stream_name }}**" args="{{ stream.get_init_args_json }}" kwargs="{{ stream.get_init_kwargs_json }}"></turbo-channels-stream-source>

    site-packages/django/core/signing.py, line 117, in dumps data = serializer().dumps(obj)

    Model: class MyModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    opened by meesix 1
  • Generate HTML from JS before sending it over the wire

    Generate HTML from JS before sending it over the wire

    Hi Everyone,

    Is it possible to run javascript to create html before sending it through a stream? or if this isn't, Is it possible to run javascript which arrives into the client via a stream?


    Currently it seems that any JS which is sent over via a stream just comes through as the plain text, without being ran before sending (example 1) or after arrival (example 2).

    Example 1 - Not running JS before message.html `

     {% for message in room.messages.all %}
            {id: {{message.id}}, content: '{{message.text}}', start: '{{message.created_at.date}}'},
    {% endfor %}
    
    <div id="visualization"></div>
        <script type="text/javascript">
    
          (function (room, vis_id) {
    
            // DOM element where the Timeline will be attached
            var container = document.getElementById(vis_id);
    
            // Create a DataSet (allows two way data-binding)
            var items = new vis.DataSet([
                        {% for message in room.messages.all %}
                            {id: {{message.id}}, content: '{{message.text}}', start: '{{message.created_at.date}}'},
                        {% endfor %}
                      ]);
    
            // Configuration for the Timeline
            var options = {};
    
            // Create a Timeline
            var timeline = new vis.Timeline(container, items, options)
    
            })();
        </script>
    
    `

    Comes out in the browser as

    `

    {id: 85, content: 'Test Message', start: 'Dec. 1, 2022'},
    {id: 86, content: 'asd', start: 'Dec. 1, 2022'},
           
    
    <div id="visualization"></div>
    
    <script type="text/javascript">
    
          (function (room) {
    
            // DOM element where the Timeline will be attached
            var container = document.getElementById(visualization);
    
            // Create a DataSet (allows two way data-binding)
            var items = new vis.DataSet([                        
    
                            {id: 85, content: 'Test Message', start: 'Dec. 1, 2022'},
                        
                            {id: 86, content: 'asd', start: 'Dec. 1, 2022'},
                        
                      ]);
    
            // Configuration for the Timeline
            var options = {};
    
            // Create a Timeline
            var timeline = new vis.Timeline(container, items, options)
    
            })();
        </script>
    
    ` As can be seen, the stream is functioning as expected for the django information, however otherwise the html otherwise just copied across, without populating the related div.

    The exact same code is used to load the visualisation on the initial page load, and works correctly.

    Example 2 - Not running JS after template.html `

            {% for message in room.messages.all %}
                {id: {{message.id}}, content: '{{message.text}}', start: '{{message.created_at.date}}'},
            {% endfor %}
    
        <script type="text/javascript">
              console.log('Test');
        </script>
    
    `

    Comes out in the browser as

    `

    {id: 85, content: 'Test Message', start: 'Dec. 1, 2022'},
    {id: 86, content: 'asd', start: 'Dec. 1, 2022'},
            
    <div id="visualization"></div>
    
    <script type="text/javascript">
           console.log('Test');
    </script>
    
    ` with nothing in the console.

    I've tried several different things with none of them working, but may have made mistakes when attempting them:

    1. Putting the Javascript in different places, for example in a django variable which can then be called - I imagine this didn't work due to potential security issues of being able to run potentially unwanted JS.
    2. Editing turbo to use render() rather than render_to_string() in order to include the content_type variable in the template, and then trying to render a JS file with the appropriate mime type.
    3. Loading the page with selenium, copying the resulting html into a file, then loading that as a template (not a scalable option, but was simply testing it).
    4. Running javascript in python using js2py, which doesnt work as the construction of the visualisation requires the use of document.getElementById, which I haven't been able to figure out how to connect it to a DOM.
    5. I've tried using the turbo.TurboRender.response() as described here
    6. I've tried using the FrameElement.reload() function - though didn't seem to have any luck getting it working at all, so my use may be off

    Breaking it down, I think it would be possible to do if I was able to import the existing DOM, or a constructed one with something like dominate, into a javascript instance or reload the frame using the javascript

    Any advice or pointers would be greatly appreciated!

    opened by RobSisson 0
  • Docs should be better.

    Docs should be better.

    I can't get turbo_django (Model)streams to work. Components work, but my ModelStream refuses doing what I want. I suppose there are some errors in the documentation, and at least it should be improved here.

    1. First, at templates.rst, you say room.channel.append(... - room in this case (a ModelStream instance) has no "channel" attr. Maybe you mean "stream" here? But there are other things that are missing. "..., or pass a Django instance..." is a bit weird. I think you mean a Model instance that has a stream attached?
    2. RoomListChannel isn't anything that can be referred to in the tutorial before, and is never explained. Is this a stream?
    3. The turbo_subscribe tag section should be before the usage of it.

    If you help me a bit to understand I could try to reorganize and corect this page for "newcomers" like me. But I need help - because, like I said, using the docs, I can't get ModelStream to work...

    opened by nerdoc 2
  • HTMX-like actions for turbo-django

    HTMX-like actions for turbo-django

    This is just a question/idea. I came across many frameworks in the last year, from sockpuppet to Unicorn and HTMX/Django. one of the things all of them offer is that actions like triggering responses can be started from any HTML element, not just by forms/buttons and anchors. E.g. changing the value of a checkbox could trigger a frame reload. This is not conveniently possible with turbo-django - however, it could be done IMHO with stimulus - or something like alpine.js.

    Is there any chance that this will be possible with turbo-django?

    opened by nerdoc 1
  • howto compile docs

    howto compile docs

    I tried to add some hints in the docs - but I want to compile the sphinx/docs locally first. But when I go into the doc dir, start automake.sh and change something, I get the error:

    WARNING: autodoc: failed to import class 'components.BroadcastComponent' from module 'turbo'; the following exception was raised:
    Traceback (most recent call last):
      File "/.../turbo-django/.venv/lib/python3.10/site-packages/sphinx/ext/autodoc/importer.py", line 62, in import_module
        return importlib.import_module(modname)
    [...]
      File "/.../turbo-django/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 63, in _setup
        raise ImproperlyConfigured(
    django.core.exceptions.ImproperlyConfigured: Requested setting SECRET_KEY, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
    [...]
    

    This seems to me that the docs try to import Django. One solution is to (fake) import django in conf.py - but I don't know how you do that in your environment. @scuml could you provide some hints how to do that?

    If you want I can add a section for testing as well then - pytest runs fine here localy for the whole suite.

    opened by nerdoc 0
  • add button click action example to documentation

    add button click action example to documentation

    like #13, it would be extremely helpful to add some more examples to the documentation, e.g. How to implement a simple <button> that does something in the backend and returns HTML. ATM there is only the <turbo-frame> with a form.

    A great example would be adding a "delete" button to the messages, with an id etc.

    Is this even possible without a form with turbo-django?

    opened by nerdoc 0
Releases(release/0.3.0)
  • release/0.3.0(Mar 27, 2022)

    • Stream class added to explicitly declare streams
      • Streams auto-detected in streams.py
      • TurboMixin has been removed. ModelStreams replace this functionality with linked model declared in Meta.model
    • Permissions can now be written by overriding the Stream.user_passes_test() method
    • Support for stream-less turbo-frame responses to POST requests
    Source code(tar.gz)
    Source code(zip)
  • 0.2.5(Dec 5, 2021)

    What's Changed

    • Turbo 7.1.0 library by @scuml in https://github.com/hotwire-django/turbo-django/pull/47

    Full Changelog: https://github.com/hotwire-django/turbo-django/compare/0.2.4...0.2.5

    Source code(tar.gz)
    Source code(zip)
  • 0.2.4(Sep 5, 2021)

  • 0.2.3(Aug 16, 2021)

    Major update to the turbo-django library. This includes breaking API changes but makes the library much more flexible to use.

    Broadcasting now centers around Turbo objects. Create a Turbo object with the broadcast name, render a template, and then call one of the actions to send the html to the subscribed clients.

    from turbo import Turbo
    
    # Send a message with the current timestamp to the channel `broadcast_name` and
    # update any element with the id 'broadcast_box`.
    Turbo('broadcast_name').render_from_string(
        f"{datetime.now()}: This is a broadcast."
    ).update(id="broadcast_box")
    
    # or for a model instance
    
    room = Room.objects.first()
    
    Turbo(room).render(
        "template.html", context={}
    ).append(id="broadcast_box")
    
    # If using TurboMixin
    
    room.turbo.render(
        "template.html", context={}
    ).append(selector="p.broadcast_class")
    
    

    broadcast_to and broadcast_self were removed as they placed severe limitations on what could be broadcast. They have been replaced with ModelBroadcasts. ModelBroadcasts live in the file broadcasts.py and look like this:

    @turbo.register(Message)
    class MessageBroadcast(turbo.ModelBroadcast):
    
        def on_save(self, message, created, *args, **kwargs):
            if created:
                message.room.turbo.render("chat/message.html", {"message": message}).append(id="messages")
            else:
                message.room.turbo.render("chat/message.html", {"message": message}).replace(id=f"message-{message.id}")
    
        def on_delete(self, message, *args, **kwargs):
            message.room.turbo.remove(id=f"message-{message.id}")
    

    This allows the user to explicitly send as many templates to as many channels as needed - and allows additional context to be sent to the template - all in a django-esque easy-to-read class.

    Other changes include:

    • Huge documentation update including a quickstart page and five-part tutorial.
    • {% turbo_stream_from %} has been renamed to {% turbo_subscribe %} and can now accept a list of channels to listen
    • BroadcastableMixin has been renamed TurboMixin and simply adds a .turbo attribute to the model instance.
    Source code(tar.gz)
    Source code(zip)
Owner
Hotwire for Django
Support for @hotwired in Django
Hotwire for Django
Program to send ROM files to Turbo Everdrive; reverse-engineered and designed to be platform-independent

PCE_TurboEverdrive_USB What is this "TurboEverdrive USB" thing ? For those who have a TurboEverdrive v2.x from krikzz.com, there was originally an opt

David Shadoff 10 Sep 18, 2022
Write-ups for CTF Internacional MetaRed 2021 5th stage

MetaRed2021-5th-Writeups Write-ups for CTF Internacional MetaRed 2021 5th stage Easy (15) No Status Category Name Creator(s) 01 Done osint Cybersecuri

UA Cybersecurity 2 Dec 22, 2021
Scraper pour les offres de stage Tesla et les notes sur Oasis (Polytech Paris-Saclay) sous forme de bot Discord

Scraper pour les offres de stage Tesla et les notes sur Oasis (Polytech Paris-Saclay) sous forme de bot Discord

Alexandre Malfreyt 1 Jan 21, 2022
Home Assistant integration for spanish electrical data providers (e.g., datadis)

homeassistant-edata Esta integración para Home Assistant te permite seguir de un vistazo tus consumos y máximas potencias alcanzadas. Para ello, se ap

VMG 163 Jan 5, 2023
This application demonstrates IoTVAS device discovery and security assessment API integration with the Rapid7 InsightVM.

Introduction This repository hosts a sample application that demonstrates integrating Firmalyzer's IoTVAS API with the Rapid7 InsightVM platform. This

Firmalyzer BV 4 Nov 9, 2022
A QGIS integration plugin for Kart repositories

QGIS Kart Plugin A plugin to work with Kart repositories Installation The Kart plugin is available in the QGIS Plugins server. To install the latest v

Koordinates 27 Jan 4, 2023
Tutor plugin for integration of Open edX with a Richie course catalog

Richie plugin for Tutor This is a plugin to integrate Richie, the learning portal CMS, with Open edX. The integration takes the form of a Tutor plugin

Overhang.IO 2 Sep 8, 2022
Sardana integration into the Jupyter ecosystem.

sardana-jupyter Sardana integration into the Jupyter ecosystem.

Marc Espín 1 Dec 23, 2021
Integration of CCURE access control system with automation HVAC of a commercial building

API-CCURE-Automation-Quantity-Floor Integration of CCURE access control system with automation HVAC of a commercial building CCURE is an access contro

Alexandre Edson Silva Pereira 1 Nov 24, 2021
A PowSyBl and Python integration based on GraalVM native image

PyPowSyBl The PyPowSyBl project gives access PowSyBl Java framework to Python developers. This Python integration relies on GraalVM to compile Java co

powsybl 23 Dec 14, 2022
Solcast Integration for Home Assistant

Solcast Solar Home Assistant(https://www.home-assistant.io/) Component This custom component integrates the Solcast API into Home Assistant. Modified

Greg 45 Dec 20, 2022
API for SpeechAnalytics integration with FreePBX/Asterisk

freepbx_speechanalytics_api API for SpeechAnalytics integration with FreePBX/Asterisk Скопировать файл settings.py.sample в settings.py и отредактиров

Iqtek, LLC 3 Nov 3, 2022
Integration between the awesome window manager and the firefox web browser.

Integration between the awesome window manager and the firefox web browser.

contribuewwt 3 Feb 2, 2022
A server shell for you to play with Powered by Django + Nginx + Postgres + Bootstrap + Celery.

A server shell for you to play with Powered by Django + Nginx + Postgres + Bootstrap + Celery.

Mengting Song 1 Jan 10, 2022
Comprehensive OpenAPI schema generator for Django based on pydantic

??️ Djagger Automated OpenAPI documentation generator for Django. Djagger helps you generate a complete and comprehensive API documentation of your Dj

null 13 Nov 26, 2022
Integration of Hotwire's Turbo library with Flask.

turbo-flask Integration of Hotwire's Turbo library with Flask, to allow you to create applications that look and feel like single-page apps without us

Miguel Grinberg 240 Jan 6, 2023
Fully Automated YouTube Channel ▶️with Added Extra Features.

Fully Automated Youtube Channel ▒█▀▀█ █▀▀█ ▀▀█▀▀ ▀▀█▀▀ █░░█ █▀▀▄ █▀▀ █▀▀█ ▒█▀▀▄ █░░█ ░░█░░ ░▒█░░ █░░█ █▀▀▄ █▀▀ █▄▄▀ ▒█▄▄█ ▀▀▀▀ ░░▀░░ ░▒█░░ ░▀▀▀ ▀▀▀░

sam-sepiol 249 Jan 2, 2023
Meta package to combine turbo-django and stimulus-django

Hotwire + Django This repository aims to help you integrate Hotwire with Django ?? Inspiration might be taken from @hotwired/hotwire-rails. We are sti

Hotwire for Django 31 Aug 9, 2022
Hotwired/Turbo Django response helpers

This package provides helpers for server-side rendering of Hotwired/Turbo streams and frames. Disclaimer: the Hotwired/Turbo client libraries are, at

Hotwire for Django 66 Apr 7, 2022
Code for Two-stage Identifier: "Locate and Label: A Two-stage Identifier for Nested Named Entity Recognition"

Code for Two-stage Identifier: "Locate and Label: A Two-stage Identifier for Nested Named Entity Recognition", accepted at ACL 2021. For details of the model and experiments, please see our paper.

tricktreat 87 Dec 16, 2022