Components

django-unicorn uses the term "component" to refer to a set of interactive functionality that can be put into templates. A component consists of a Django HTML template with specific tags and a Python class which provides the backend code for the template.

The easiest way to create your first component is to run the following Django management command after django-unicorn is installed.

python manage.py startunicorn hello-world

If this is the first component you create, you will also need to add "unicorn", to INSTALLED_APPS in your Django settings file (normally settings.py) to make sure that Django can find the component templates you create.

Add {% unicorn 'hello-world' %} into the template you want to load the new component.

Component arguments

kwargs can be passed into the unicorn templatetag from the template. The kwargs will be available in the component __init__ method.

When overriding __init__ calling super().__init__(**kwargs) is required for the component to initialize properly.

# hello_world.py
from django_unicorn.components import UnicornView

class HelloWorldView(UnicornView):
    name = "World"

    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)  # calling super is required
        self.name = kwargs.get("name")
<!-- index.html -->
{% unicorn 'hello-world' name="Universe" %}

Regular Django template variables can also be passed in as an argument as long as it is available in the template context.

# views.py
from django.shortcuts import render

def index(request):
    context = {"hello": {"world": {"name": "Galaxy"}}}
    return render(request, "index.html", context)
<!-- index.html -->
{% unicorn 'hello-world' name=hello.world.name %}

Example component

A basic example component could consist of the following template and class.

# hello_world.py
from django_unicorn.components import UnicornView

class HelloWorldView(UnicornView):
    name = "World"
<!-- hello-world.html -->
<div>
    <input unicorn:model="name" type="text" id="text">
    Hello {{ name|title }}
</div>

unicorn:model is the magic that ties the input to the backend component. The Django template variable can use any property or method on the component as if they were context variables passed in from a view. The attribute passed into unicorn:model refers to the property in the component class and binds them together.

By default unicorn:model updates are triggered by listening to input events on the element. To listen for the blur event instead, use the lazy modifier.

When a user types into the text input, the information is passed to the backend and populates the component class, which is then used to generate the output of the template HTML. The template can use any normal Django templatetags or filters (e.g. the title filter above).

Unicorn attributes

Attributes used in component templates usually start with unicorn:, however the shortcut u: is also supported. So, for example, unicorn:model could also be written as u:model.

Supported property types

Properties of the component can be of many different types, including str, int, list, dictionary, Django Model, Django QuerySet, or custom classes.

Dictionary and Django Models

Dictionaries and Django models can be used similarly by unicorn:model.

# hello_world.py
from django_unicorn.components import UnicornView
from book.models import Book
    
class HelloWorldView(UnicornView):
    book = Book.objects.get(title='American Gods')
    book_ratings = {'excellent': {'title: 'American Gods'}}
<!-- hello-world.html -->
<div>
    <input unicorn:model="book.title" type="text" id="model">
    <input unicorn:model="book_ratings.excellent.title" type="text" id="dictionary">
</div>

Django QuerySet

Django QuerySet can be referenced similarly to the Django template language in a unicorn:model.

# hello_world.py
from django_unicorn.components import UnicornView
from book.models import Book
    
class HelloWorldView(UnicornView):
    books = Book.objects.all()
<!-- hello-world.html -->
<div>
    <input unicorn:model="books.0.title" type="text" id="text">
</div>

Custom class

Custom classes need to define how they are serialized. If you have access to the object to serialize, you can define a to_json method on the object to return a dictionary that can be used to serialize. Inheriting from unicorn.components.UnicornField is a quick way to serialize a custom class, but note that it just calls self.__dict__ under the hood, so it is not doing anything smart. from_json would need to be defined to deserialize the dictionary back into the actual class.

Another option is to set the form_class on the component and utilize Django's built-in forms and widgets to handle how the class should be deserialized. More details are provided in the validation section.

# hello_world.py
from django_unicorn.components import UnicornView, UnicornField

class Author(UnicornField):
    def __init__(self):
        self.name = 'Neil Gaiman'
    
    # Not needed because inherited from `UnicornField`
    # def to_json(self):
    #    return {'name': self.name}

class HelloWorldView(UnicornView):
    author = Author()
<!-- hello-world.html -->
<div>
    <input unicorn:model="author.name" type="text" id="author_name">
</div>

Never put sensitive data into a public property because that information will publicly available in the HTML source code.