Django Models#
Unicorn
provides tight integration with Django Models
and QuerySets
to handle typical workflows.
Model#
A Django Model
can be used as a field on a component just like basic Python primitive types. Use unicorn:model
to bind to a field of a Django Model
like you would in a normal Django template.
Warning
Using this functionality will serialize your entire model by default and expose all of the values in the HTML source code. Do not use this particular functionality if there are properties that need to be kept private.
One option is to customize the serialization of the model into a dictionary to only expose the data that should be publicly available.
Another option is to use Meta.exclude or Meta.javascript_exclude so those fields are not exposed.
<!-- model.html -->
<div>
<input unicorn:model.defer="book.title" type="text" id="book" />
{{ book.title }}
<button unicorn:click="save({{ book.pk }})">Save</button>
</div>
# model.py
from django_unicorn.components import UnicornView
from books.models import Book
class ModelView(UnicornView):
book: Book = None
def mount(self):
self.book = Book.objects.all().first()
def save(self, book_to_save: Book):
book_to_save.save()
Note
The model’s pk
will be used to look up the correct model if there is only one argument for an action method and it has a type annotation for a Django Model
. To lookup by a different model field, pass a dictionary into the front-end.
<button unicorn:click="delete({ 'uuid': '{{ book.uuid }}'})">Delete by uuid</button>
def delete(self, book_to_delete: Book):
book_to_delete.delete()
QuerySet#
Django models in a QuerySet
can be accessed in a unicorn:model
with “dot notation” similar to a list
.
# hello_world.py
from django_unicorn.components import UnicornView
from book.models import Book
class HelloWorldView(UnicornView):
books = Book.objects.none()
def mount(self):
self.books = Book.objects.all()
<!-- hello-world.html -->
<div>
<input unicorn:model="books.0.title" type="text" id="text" />
</div>
An example of looping over all models in a queryset.
# queryset.py
from django_unicorn.components import UnicornView
from books.models import Book
class QuerysetView(UnicornView):
books = Book.objects.none()
def mount(self):
self.books = Book.objects.all().order_by("-id")[:5]
def save(self, book_idx: int):
self.books[book_idx].save()
<!-- queryset.html -->
<div>
{% for book in books %}
<div>
<div>
<input unicorn:model.defer="books.{{ forloop.counter0 }}.title" type="text" id="title" />
{{ book.title }}
</div>
<div>
<input unicorn:model.defer="books.{{ forloop.counter0 }}.description" type="text" id="description" />
{{ book.description }}
</div>
<div>
<button unicorn:click="save({{ forloop.counter0 }})">Save</button>
</div>
</div>
{% endfor %}
</div>
Warning
This will expose all of the model values for the QuerySet
in the HTML source. One way to avoid leaking all model information is to pass the fields that are publicly viewable into values()
on your QuerySet
.
# queryset.py
from django_unicorn.components import UnicornView
from books.models import Book
class QuerysetView(UnicornView):
books = Book.objects.none()
def mount(self):
self.books = Book.objects.all().order_by("-id").values("pk", "title")
A QuerySetType
type hint can be used to ensure the correct QuerySet
is used for the component field.
# queryset.py
from django_unicorn.components import QuerySetType, UnicornView
from books.models import Book
class QuerysetView(UnicornView):
books: QuerySetType[Book] = None
def mount(self):
self.books = Book.objects.all().order_by("-id")[:5]
def save(self, book_idx: int):
self.books[book_idx].save()