Skip to main content

Prototype and debug `Unicorn` components without creating a complete Django application.

Project description

django-unicorn-playground 🦄🛝

The Unicorn Playground provides a way to prototype and debug Unicorn components without creating a complete Django application. It can either be run as a standalone script or by installing the library.

Standalone script

The benefit of the standalone script is that inline script metadata provides the information for pipx to create a virtual environment and install any dependencies automatically.

Create an example component

  1. Install pipx
  2. Create a new file called counter.py with the following code
# /// script
# requires-python = ">=3.10"
# dependencies = [
#   "django-unicorn-playground"
# ]
# ///

from django_unicorn.components import UnicornView

class CounterView(UnicornView):
    template_html = """<div>
    <div>
        Count: {{ count }}
    </div>

    <button unicorn:click="increment">+</button>
    <button unicorn:click="decrement">-</button>
</div>
"""

    count: int

    def increment(self):
        count += 1
    
    def decrement(self):
        count -= 1

if __name__ == "__main__":
    from django_unicorn_playground import UnicornPlayground

    UnicornPlayground(__file__).runserver()
  1. pipx run counter.py
  2. Go to https://localhost:8000

Library CLI

The django-unicorn-playground library can also be installed to provide a command-line interface to try a component. This approach uses basically the same code, but doesn't require the script inline metadata or the call to runserver at the end -- the CLI takes care of running the development server directly. Those portions can be included, though, and will not prevent the CLI from working as expected.

Create an example component

  1. pipx install django-unicorn-playground
  2. Create counter.py with the following code:
from django_unicorn.components import UnicornView

class CounterView(UnicornView):
    template_html = """<div>
    <div>
        Count: {{ count }}
    </div>

    <button unicorn:click="increment">+</button>
    <button unicorn:click="decrement">-</button>
</div>
"""

    count: int

    def increment(self):
        count += 1
    
    def decrement(self):
        count -= 1
  1. unicorn counter.py
  2. Go to https://localhost:8000

Command-line options

port

Port for the developer webserver. Defaults to 8000. Required to be an integer.

template_dir

Directory for templates. Required to be a string path.

version

Shows the current version.

help

Show the available CLI options.

Example components

There are a few example components in the examples directory.

They can be run with something like pipx run --no-cache examples/counter.py.

Template HTML

The component's HTML can be initialized in a few ways.

UnicornView.template_html attribute

The HTML can be set with a class-level template_html field.

from django_unicorn.components import UnicornView

class TestView(UnicornView):
    template_html = """<div>
    <div>
        Count: {{ count }}
    </div>

    <button unicorn:click="increment">+</button>
    <button unicorn:click="decrement">-</button>
</div>
"""

    ...

UnicornView.template_html method

The HTML can be returned from a template_html instance method.

from django_unicorn.components import UnicornView

class TestView(UnicornView):
    def template_html(self): 
        return """<div>
    <div>
        Count: {{ count }}
    </div>

    <button unicorn:click="increment">+</button>
    <button unicorn:click="decrement">-</button>
</div>
"""

    ...

HTML file

Similar to a typical django-unicorn setup, the component HTML can be a separate template file. This is the fallback and will only be searched for if the template_view field or method is not defined on the component.

  1. cd to the same directory as the component Python file you created
  2. mkdir -p templates/unicorn
  3. touch {COMPONENT-NAME}.html, e.g. for a component Python named counter.py create counter.html
  4. Add the component HTML to the newly created file

Template directory

By default django-unicorn-playground will look in a templates/unicorn folder for templates. The template location can be changed by passing in template_dir into the UnicornPlayground() constructor or adding a --template_dir argument to the CLI.

index.html

The root URL dynamically creates index.html which creates HTML for the component. It looks something like the following.

{% extends "base.html" %}
{% block content %}
{% unicorn 'COMPONENT-NAME' %}
{% endblock content %}

It can be overridden by creating a custom index.html in the template directory.

base.html

By default, index.html extends base.html. It can be overridden by creating a custom base.html in the template directory.

Using a Python HTML builder

Any Python library that generates normal HTML strings works great with django-unicorn-playground.

haitch

from django_unicorn.components import UnicornView
import haitch

class TestComponent(UnicornView)
   def template_html(self):
       return haitch.div()(
           haitch.button("Increment +", **{"unicorn:click": "increment"}),
           haitch.button("Decrement -", **{"unicorn:click": "decrement"}),
           haitch.div("{{ count }}"),
       )
   
   ...

htpy

from django_unicorn.components import UnicornView
import htpy

class TestComponent(UnicornView)
   def template_html(self):
       return htpy.div()[
           htpy.button({"unicorn:click": "increment"})["Increment +"],
           htpy.button({"unicorn:click": "decrement"})["Decrement -"],
           htpy.div()["{{ count }}"],
       ]
   
   ...

dominate

from django_unicorn.components import UnicornView
from dominate import tags as dom

class TestComponent(UnicornView)
   def template_html(self):
       return dom.div(
           dom.button("Increment +", **unicorn.click(self.increment)),
           dom.button("Decrement -", **unicorn.click(self.decrement)),
           dom.div("{{ count }}"),
       )
   
   ...

Unicorn HTML helpers

When using an HTML builder, django-unicorn-playground provides a few helper methods which make it a little cleaner to create Unicorn-specific HTML.

For example, if using haitch instead of doing this:

import haitch
from django_unicorn.components import UnicornView

class TestComponent(UnicornView)
   def template_html(self):
       return haitch.div()(
           haitch.button("Increment +", **{"unicorn:click": "increment"}),
           haitch.button("Decrement -", **{"unicorn:click": "decrement"}),
           haitch.div("{{ count }}"),
       )
   
   ...

This is how it would work with the helper methods:

import haitch
from django_unicorn.components import UnicornView
from django_unicorn_playground.html import django, unicorn

class TestComponent(UnicornView)
   def template_html(self):
       return haitch.div()(
           haitch.button("Increment +", **unicorn.click(self.increment)),
           haitch.button("Decrement -", **unicorn.click(self.decrement)),
           haitch.div(django.variable("count")),
       )
   
   ...

Local development

Standalone script

Using the inline script metadata with pipx seems a little quirky and I could not get editable installs working reliably. I also tried hatch run which had its own issues. Not sure if there are other (read: better) approaches.

As far as I can tell, the best approach is to use an absolute file path like "django_unicorn_playground @ file:///Users/adam/Source/adamghill/django-unicorn-playground/dist/django_unicorn_playground-0.1.0-py3-none-any.whl" (note the triple forward-slash after "file:") as a dependency, and rebuilding and re-running the script without any caching like this: poetry build && pipx run --no-cache examples/counter.py any time you make a code change.

Note: you will need to update the component's dependency so it points to the path and version on your machine.

There is a just command to make testing a standalone script slightly less painful during local development.

  1. Install just
  2. just serve {COMPONENT-FILE-PATH}, e.g. just serve examples/counter.py

CLI

  1. poetry install
  2. poetry run unicorn {COMPONENT-FILE-PATH}, e.g. poetry run unicorn examples/counter.py

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_unicorn_playground-0.1.1.tar.gz (10.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_unicorn_playground-0.1.1-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file django_unicorn_playground-0.1.1.tar.gz.

File metadata

  • Download URL: django_unicorn_playground-0.1.1.tar.gz
  • Upload date:
  • Size: 10.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.6.1 CPython/3.10.8 Darwin/23.2.0

File hashes

Hashes for django_unicorn_playground-0.1.1.tar.gz
Algorithm Hash digest
SHA256 11d09e254c9cfb3c8490a7fc760f609b5f99d8651d04c197ed5dc6390ac45f65
MD5 18afb7be8b0c6d034d35f03dbbdd371a
BLAKE2b-256 a332750bfb234e9351687ae02df56f0468f6b4f5df127e5f8aa4df89f1a3daf2

See more details on using hashes here.

File details

Details for the file django_unicorn_playground-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_unicorn_playground-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fce7b4ffb72b1d732e45e60b8aa81883bbba339007a0060f72ad0f9220e77a58
MD5 9bb03872d7cbd52799024c11cb0f43c1
BLAKE2b-256 57ac2f81d9b728ab738a8505f3b6da54856485130248e772c27f5af1c226967f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page