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
- Install
pipx - Create a new file called
counter.pywith 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()
pipx run counter.py- 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
pipx install django-unicorn-playground- Create
counter.pywith 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
unicorn counter.py- 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.
cdto the same directory as the component Python file you createdmkdir -p templates/unicorntouch {COMPONENT-NAME}.html, e.g. for a component Python namedcounter.pycreatecounter.html- 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.
- Install just
just serve {COMPONENT-FILE-PATH}, e.g.just serve examples/counter.py
CLI
poetry installpoetry run unicorn {COMPONENT-FILE-PATH}, e.g.poetry run unicorn examples/counter.py
Acknowledgments
Metadata
Release files for django-unicorn-playground 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django_unicorn_playground-0.1.1.tar.gz | 10.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_unicorn_playground-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.7 kB
Release files / django_unicorn_playground-0.1.1.tar.gz
| Download URL | django_unicorn_playground-0.1.1.tar.gz |
|---|---|
| Size | 10.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11d09e254c9cfb3c8490a7fc760f609b5f99d8651d04c197ed5dc6390ac45f65
|
|
BLAKE2b-256 checksum How to use checksums |
a332750bfb234e9351687ae02df56f0468f6b4f5df127e5f8aa4df89f1a3daf2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.6.1 CPython/3.10.8 Darwin/23.2.0
|
Release files / django_unicorn_playground-0.1.1-py3-none-any.whl
| Download URL | django_unicorn_playground-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fce7b4ffb72b1d732e45e60b8aa81883bbba339007a0060f72ad0f9220e77a58
|
|
BLAKE2b-256 checksum How to use checksums |
57ac2f81d9b728ab738a8505f3b6da54856485130248e772c27f5af1c226967f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.6.1 CPython/3.10.8 Darwin/23.2.0
|
Security for django-unicorn-playground 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.