You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/source/changelog.md
+29Lines changed: 29 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,34 @@
1
1
# Changelog
2
2
3
+
## 0.66.0
4
+
5
+
**Features**
6
+
- Implement `unicorn:dirty` targeting support.
7
+
- Add signals for lifecycle hooks.
8
+
- Implement script tag loading from newly added child components.
9
+
- Implement popstate support for history navigation.
10
+
- Implement a timestamp-based (epoch) tracking mechanism to identify and discard stale responses on the frontend.
11
+
12
+
**Fixes**
13
+
- Surface broken import errors when loading components.
14
+
- Handle nested method calls in `parse_call_method_name`.
15
+
- Restore float values in nested `tuple[dict]` fields.
16
+
- Fix: `u:loading` on child elements no longer disables parent `u:click`.
17
+
- Fix: Trigger loading states for actions called via `Unicorn.call()`.
18
+
- Fix: Include child component JavaScript calls in response.
19
+
- Fix `ValueError` when deserializing `ForeignKey` fields on models loaded outside `mount`.
20
+
21
+
**Refactors**
22
+
- Consolidate component metadata (checksum, hash, epoch) into 'meta' identifier.
23
+
- Remove redundant per-component script tags and enable global DOM scanning.
24
+
25
+
**Documentation**
26
+
- Add tutorial documentation covering inputs, actions, and polling.
27
+
- Add guide on table structure limitations.
28
+
- Clarify Unicorn can be adopted incrementally.
29
+
- Add Context Processors and Component Re-rendering section.
30
+
- Improve Installation Tutorial and Add Pagination Documentation.
31
+
3
32
## 0.65.0
4
33
5
34
- Properly escape single quotes in Unicorn.call() arguments [#773](https://github.com/adamghill/django-unicorn/pull/773) by [JohananOppongAmoateng](https://github.com/JohananOppongAmoateng).
Copy file name to clipboardExpand all lines: docs/source/templates.md
+107Lines changed: 107 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,6 +65,113 @@ class HelloWorldView(UnicornView):
65
65
[Django models](django-models.md) has many more details about using Django models in `Unicorn`.
66
66
```
67
67
68
+
## Models inside `{% for %}` loops
69
+
70
+
When iterating over a list with a Django `{% for %}` loop, the loop variable is **not** the same as the component attribute that holds the list. Unicorn syncs `<input>` values by looking up the `unicorn:model` name in the component's serialised data, so the model name must use the **component attribute path** (e.g. `items.0.name`), not the loop variable name (e.g. `item.name`).
71
+
72
+
Use `{{ forloop.counter0 }}` to build the correct index-based path:
The key is `items.{{ forloop.counter0 }}.name` this renders to `items.0.name`, `items.1.name`, etc., which Unicorn can resolve directly against the component data.
108
+
109
+
```{warning}
110
+
Using the **loop variable name** in `unicorn:model` (e.g. `unicorn:model="item.name"`)
111
+
will **not** work correctly after a re-render. When the component re-renders,
112
+
Unicorn tries to look up `item` in the component's data, but `item` is only a
113
+
Django template loop variable — it has no corresponding key in the serialised
114
+
component state. As a result morphdom will clear the input's value.
115
+
116
+
Use `items.{{ forloop.counter0 }}.name` instead.
117
+
```
118
+
119
+
## Rendering choice fields
120
+
121
+
Django models often use `TextChoices` (or `IntegerChoices`) to constrain a field to a fixed set of values. To render a reactive `<select>` that is bound to a Unicorn component field, expose the choices list as a component attribute and exclude it from the JavaScript context (since the choices are static and do not need to be reactive).
122
+
123
+
```python
124
+
# new_course.py
125
+
from django_unicorn.components import UnicornView
126
+
from curriculum.models import Course
127
+
128
+
129
+
classNewCourseView(UnicornView):
130
+
grade_level = Course.GradeLevel.UNDEFINED
131
+
subject = Course.Subject.UNDEFINED
132
+
133
+
# Static choices — kept out of the JSON state sent to the browser
Adding the choices to [`Meta.javascript_exclude`](views.md#javascript_exclude) keeps them in the Django template context (so the `{% for %}` loop works) without serialising them into the `unicorn:data` JSON attribute on every render. The `grade_level` and `subject` fields remain fully reactive — Unicorn syncs the selected value back to the component on each change.
167
+
168
+
```{note}
169
+
If you also need validation, set `form_class` on the component to a Django `ModelForm` or `Form`.
170
+
Unicorn will validate `grade_level` and `subject` against the form's field definitions when
171
+
`$validate` is called or when an action method calls `self.validate()`. See
Copy file name to clipboardExpand all lines: docs/source/views.md
+53-1Lines changed: 53 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -205,6 +205,45 @@ class HelloKwargView(UnicornView):
205
205
assertself.component_kwargs["hello"] =="World"
206
206
```
207
207
208
+
### Passing a Django Form
209
+
210
+
A Django `Form` (or `ModelForm`) instance can be passed directly from a template into a unicorn component as a keyword argument. The form will be available in the component's template context for rendering, but it is automatically excluded from the JSON state sent to the browser (since forms cannot be serialized to JSON).
# self.form is available here on the initial render
226
+
pass
227
+
```
228
+
229
+
```html
230
+
<!-- unicorn/my-form-component.html -->
231
+
<div>
232
+
<formmethod="POST">
233
+
{% csrf_token %}
234
+
{{ form.as_p }}
235
+
<buttonunicorn:click="submit">Submit</button>
236
+
</form>
237
+
</div>
238
+
```
239
+
240
+
```{note}
241
+
Because forms cannot be pickled, `self.form` will be `None` on subsequent AJAX
242
+
interactions (after the initial page load). If you need to process submitted form
243
+
data reactively, declare a `form_class` on the component and use
244
+
[component validation](validation.md) instead.
245
+
```
246
+
208
247
### request
209
248
210
249
The current `request`.
@@ -402,7 +441,12 @@ class HelloStateView(UnicornView):
402
441
403
442
### javascript_exclude
404
443
405
-
To allow an attribute to be included in the the context to be used by a Django template, but not exposed to JavaScript, add it to the `Meta` class's `javascript_exclude` tuple.
444
+
To allow an attribute to be included in the context to be used by a Django template, but not exposed to JavaScript, add it to the `Meta` class's `javascript_exclude` tuple.
445
+
446
+
```{note}
447
+
Django `Form` and `ModelForm` instances are **automatically** excluded from the
448
+
JavaScript context — you do not need to add them to `javascript_exclude`.
449
+
```
406
450
407
451
```html
408
452
<!-- hello-state.html -->
@@ -582,4 +626,12 @@ Do not store unpickleable objects (e.g. generators) on the component instance.
582
626
583
627
If you need to use an unpickleable object, either convert it to a pickleable type (e.g. convert a generator to a list) or re-initialize it within the method that needs it without storing it on `self`.
584
628
629
+
```{note}
630
+
Django `Form` and `ModelForm` instances are handled automatically — they are stripped
631
+
from the component before pickling and restored afterwards, so passing a form as a
632
+
template kwarg (see [Passing a Django Form](#passing-a-django-form)) will not cause
633
+
pickling errors. The form will be `None` after a cache restore (i.e. on subsequent
0 commit comments