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
@@ -195,6 +195,45 @@ class HelloKwargView(UnicornView):
195
195
assertself.component_kwargs["hello"] =="World"
196
196
```
197
197
198
+
### Passing a Django Form
199
+
200
+
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
216
+
pass
217
+
```
218
+
219
+
```html
220
+
<!-- unicorn/my-form-component.html -->
221
+
<div>
222
+
<formmethod="POST">
223
+
{% csrf_token %}
224
+
{{ form.as_p }}
225
+
<buttonunicorn:click="submit">Submit</button>
226
+
</form>
227
+
</div>
228
+
```
229
+
230
+
```{note}
231
+
Because forms cannot be pickled, `self.form` will be `None` on subsequent AJAX
232
+
interactions (after the initial page load). If you need to process submitted form
233
+
data reactively, declare a `form_class` on the component and use
234
+
[component validation](validation.md) instead.
235
+
```
236
+
198
237
### request
199
238
200
239
The current `request`.
@@ -392,7 +431,12 @@ class HelloStateView(UnicornView):
392
431
393
432
### javascript_exclude
394
433
395
-
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.
434
+
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.
435
+
436
+
```{note}
437
+
Django `Form` and `ModelForm` instances are **automatically** excluded from the
438
+
JavaScript context — you do not need to add them to `javascript_exclude`.
439
+
```
396
440
397
441
```html
398
442
<!-- hello-state.html -->
@@ -465,4 +509,12 @@ Do not store unpickleable objects (e.g. generators) on the component instance.
465
509
466
510
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`.
467
511
512
+
```{note}
513
+
Django `Form` and `ModelForm` instances are handled automatically — they are stripped
514
+
from the component before pickling and restored afterwards, so passing a form as a
515
+
template kwarg (see [Passing a Django Form](#passing-a-django-form)) will not cause
516
+
pickling errors. The form will be `None` after a cache restore (i.e. on subsequent
0 commit comments