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 dependencies are defined in the file and pipx
handles creating the virtual environment when the script is running.
- Install
pipx
- Create a new file called
counter.py
- Add the following code to
counter.py
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "django-unicorn-playground"
# ]
# ///
from django_unicorn.components import UnicornView
from django_unicorn_playground import UnicornPlayground
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__":
UnicornPlayground(__file__).runserver()
pipx run counter.py
- Go to https://localhost:8000
CLI
Another option is to install the django-unicorn-playground
library. This provides a CLI that can be used to run components. 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 being usable.
pipx install django-unicorn-playground
- 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
unicorn counter.py
- Go to https://localhost:8000
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_file 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_file method
The HTML can be returns 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.
cd
to the same directory as the component Python file you createdmkdir -p templates/unicorn
touch {COMPONENT-NAME}.html
, e.g. for a component Python namedcounter.py
createcounter.html
- Add the component HTML to the newly created file
Using a Python HTML builder
Any Python library that generates normal HTML strings work great with django-unicorn-playground
.
Some examples of libraries below:
haitch
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 }}"),
)
...
htpy
import htpy
from django_unicorn.components import UnicornView
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 dominate import tags as dom
from django_unicorn.components import UnicornView
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 a Python HTML builder like the above, there are a few helper methods which make it a little cleaner to build 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 }}"),
)
...
Using 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
Inline script metadata
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 it's own issues. Not sure if there are other 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 on your machine.
However, there is a just
command to make re-building for local dev slightly less painful.
- Install just
just serve examples/counter.py
CLI
Working locally with the CLI is more straight-forward than the inline script metadata approach.
poetry install
poetry run unicorn examples/counter.py
Acknowledgments
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_unicorn_playground-0.1.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 72075a32064db416df75a8b26db94a91afa71b3f32f0c92e70992537672de206 |
|
MD5 | 95f29a0abb657f461810051f6083c249 |
|
BLAKE2b-256 | 5257d70c3f089311f25c8b7052075b60acc782f56640e43e7031ec8598c5fb7d |
Hashes for django_unicorn_playground-0.1.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7be11ef3e9153e1716e1a51fc43ee5a2743bf2bfecae3f6454ae7ef5587ab155 |
|
MD5 | 4756139fb5275ccefd7a971c829c399c |
|
BLAKE2b-256 | ba6da034a632dc08d2013de6c8d75cc47d6abf0f8ee9023b1886e4da4ab459e4 |