Skip to content

Commit 5cf5640

Browse files
authored
v2.1.0 (#108)
- Change type hint on `view_to_component` callable to have `request` argument be optional. - More DRY way of async view rendering. - Have docs demonstrate how to use Django-IDOM with `channels>=4.0.0`. - Concatenate some examples on the `view_to_component` docs - Add note to docs about potential information exposure via `view_to_component` when using `compatibility=True`. - Clean up docs on running tests
1 parent 09d11f0 commit 5cf5640

File tree

22 files changed

+288
-182
lines changed

22 files changed

+288
-182
lines changed

CHANGELOG.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,28 @@ Using the following categories, list your changes in this order:
2424

2525
- Nothing (yet)
2626

27-
## [2.0.1]- 2022-10-18
27+
## [2.1.0] - 2022-11-01
28+
29+
## Changed
30+
31+
- Minimum `channels` version is now `4.0.0`.
32+
33+
### Fixed
34+
35+
- Change type hint on `view_to_component` callable to have `request` argument be optional.
36+
- Change type hint on `view_to_component` to represent it as a decorator with paranthesis (ex `@view_to_component(compatibility=True)`)
37+
38+
## Security
39+
40+
- Add note to docs about potential information exposure via `view_to_component` when using `compatibility=True`.
41+
42+
## [2.0.1] - 2022-10-18
2843

2944
### Fixed
3045

31-
- Ability to use `key=...` parameter on all prefabricated components
46+
- Ability to use `key=...` parameter on all prefabricated components.
3247

33-
## [2.0.0]- 2022-10-17
48+
## [2.0.0] - 2022-10-17
3449

3550
### Added
3651

@@ -155,7 +170,8 @@ Using the following categories, list your changes in this order:
155170

156171
- Support for IDOM within the Django
157172

158-
[unreleased]: https://github.com/idom-team/django-idom/compare/2.0.1...HEAD
173+
[unreleased]: https://github.com/idom-team/django-idom/compare/2.1.0...HEAD
174+
[2.1.0]: https://github.com/idom-team/django-idom/compare/2.0.1...2.1.0
159175
[2.0.1]: https://github.com/idom-team/django-idom/compare/2.0.0...2.0.1
160176
[2.0.0]: https://github.com/idom-team/django-idom/compare/1.2.0...2.0.0
161177
[1.2.0]: https://github.com/idom-team/django-idom/compare/1.1.0...1.2.0

docs/src/contribute/running-tests.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
This repo uses [Nox](https://nox.thea.codes/en/stable/) to run scripts which can be found in `noxfile.py`. For a full test of available scripts run `nox -l`. To run the full test suite simple execute:
1+
This repo uses [Nox](https://nox.thea.codes/en/stable/) to run tests. For a full test of available scripts run `nox -l`.
2+
3+
If you plan to run tests, you'll need to install the following dependencies first:
4+
5+
- [Python 3.8+](https://www.python.org/downloads/)
6+
- [Git](https://git-scm.com/downloads)
7+
8+
Once done, you should clone this repository:
9+
10+
```bash
11+
git clone https://github.com/idom-team/django-idom.git
12+
cd django-idom
13+
pip install -r ./requirements/test-run.txt --upgrade
14+
```
15+
16+
Then, by running the command below you can run the full test suite:
217

318
```
419
nox -s test
520
```
621

7-
If you do not want to run the tests in the background:
22+
Or, if you want to run the tests in the foreground:
823

924
```
1025
nox -s test -- --headed
1126
```
12-
13-
!!! warning "Most tests will not run on Windows"
14-
15-
Due to [bugs within Django Channels](https://github.com/django/channels/issues/1207), functional tests are not run on Windows. In order for Windows users to test Django-IDOM functionality, you will need to run tests via [Windows Subsystem for Linux](https://code.visualstudio.com/docs/remote/wsl).

docs/src/features/components.md

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## View To Component
66

7-
Convert any Django view into a IDOM component by usng this decorator. Compatible with sync/async [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/).
7+
Convert any Django view into a IDOM component by usng this decorator. Compatible with [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). Views can be sync or async.
88

99
=== "components.py"
1010

@@ -41,11 +41,42 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
4141
| --- | --- |
4242
| `_ViewComponentConstructor` | A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` and returns an IDOM component. |
4343

44-
??? warning "Existing limitations"
44+
??? Warning "Potential information exposure when using `compatibility = True`"
45+
46+
When using `compatibility` mode, IDOM automatically exposes a URL to your view.
47+
48+
It is your responsibility to ensure priveledged information is not leaked via this method.
49+
50+
This can be done via directly writing conditionals into your view, or by adding decorators such as [user_passes_test](https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test) to your views prior to using `view_to_component`.
51+
52+
=== "Function Based View"
53+
54+
```python
55+
...
56+
57+
@view_to_component(compatibility=True)
58+
@user_passes_test(lambda u: u.is_superuser)
59+
def example_view(request):
60+
...
61+
```
62+
63+
=== "Class Based View"
64+
65+
```python
66+
...
67+
68+
@view_to_component(compatibility=True)
69+
@method_decorator(user_passes_test(lambda u: u.is_superuser), name="dispatch")
70+
class ExampleView(TemplateView):
71+
...
72+
```
73+
74+
??? info "Existing limitations"
4575

4676
There are currently several limitations of using `view_to_component` that may be resolved in a future version of `django_idom`.
4777

4878
- Requires manual intervention to change request methods beyond `GET`.
79+
- IDOM events cannot conveniently be attached to converted view HTML.
4980
- Does not currently load any HTML contained with a `<head>` tag
5081
- Has no option to automatically intercept local anchor link (ex. `#!html <a href='example/'></a>`) click events
5182

@@ -77,87 +108,95 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
77108

78109
??? question "How do I transform views from external libraries?"
79110

80-
=== "components.py"
111+
In order to convert external views, you can utilize `view_to_component` as a function, rather than a decorator.
81112

82-
In order to convert external views, you can utilize `view_to_component` as a function, rather than a decorator.
113+
=== "components.py"
83114

84115
```python linenums="1"
85116
from idom import component, html
86117
from django.http import HttpResponse
87118
from django_idom.components import view_to_component
88119
from some_library.views import example_view
89120

90-
converted_view = view_to_component(example_view)
121+
example_vtc = view_to_component(example_view)
91122

92123
@component
93124
def my_component():
94125
return html.div(
95-
converted_view(),
126+
example_vtc(),
96127
)
97128
```
98129

99-
??? question "How do I provide `args` and `kwargs` to a view?"
130+
??? question "How do I provide `request`, `args`, and `kwargs` to a view?"
100131

101-
You can use the `args` and `kwargs` parameters to provide positional and keyworded arguments to a view.
132+
<font size="4">**`Request`**</font>
133+
134+
You can use the `request` parameter to provide the view a custom request object.
102135

103136
=== "components.py"
104137

105138
```python linenums="1"
106139
from idom import component, html
107-
from django.http import HttpResponse
140+
from django.http import HttpResponse, HttpRequest
108141
from django_idom.components import view_to_component
109142

143+
example_request = HttpRequest()
144+
example_request.method = "PUT"
145+
110146
@view_to_component
111-
def hello_world_view(request, arg1, arg2, key1=None, key2=None):
112-
return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}")
147+
def hello_world_view(request):
148+
return HttpResponse(f"Hello World! {request.method}")
113149

114150
@component
115151
def my_component():
116152
return html.div(
117153
hello_world_view(
118-
None, # Your request object (optional)
119-
"value_1",
120-
"value_2",
121-
key1="abc",
122-
key2="123",
154+
example_request,
123155
),
124156
)
125157
```
126158

127-
??? question "How do I provide a custom `request` object to a view?"
159+
---
128160

129-
You can use the `request` parameter to provide the view a custom request object.
161+
<font size="4">**`args` and `kwargs`**</font>
162+
163+
You can use the `args` and `kwargs` parameters to provide positional and keyworded arguments to a view.
130164

131165
=== "components.py"
132166

133167
```python linenums="1"
134168
from idom import component, html
135-
from django.http import HttpResponse, HttpRequest
169+
from django.http import HttpResponse
136170
from django_idom.components import view_to_component
137171

138-
example_request = HttpRequest()
139-
example_request.method = "PUT"
140-
141172
@view_to_component
142-
def hello_world_view(request):
143-
return HttpResponse(f"Hello World! {request.method}")
173+
def hello_world_view(request, arg1, arg2, key1=None, key2=None):
174+
return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}")
144175

145176
@component
146177
def my_component():
147178
return html.div(
148179
hello_world_view(
149-
example_request,
180+
None, # Your request object (optional)
181+
"value_1",
182+
"value_2",
183+
key1="abc",
184+
key2="123",
150185
),
151186
)
152187
```
153188

154-
??? question "What is `compatibility` mode?"
189+
??? question "How do I use `strict_parseing`, `compatibility`, and `transforms`?"
155190

156-
For views that rely on HTTP responses other than `GET` (such as `PUT`, `POST`, `PATCH`, etc), you should consider using compatibility mode to render your view within an iframe.
191+
<font size="4">**`strict_parsing`**</font>
157192

158-
Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments do not apply to compatibility mode.
193+
By default, an exception will be generated if your view's HTML does not perfectly adhere to HTML5.
159194

160-
Please note that by default the iframe is unstyled, and thus won't look pretty until you add some CSS.
195+
However, there are some circumstances where you may not have control over the original HTML, so you may be unable to fix it. Or you may be relying on non-standard HTML tags such as `#!html <my-tag> Hello World </my-tag>`.
196+
197+
In these scenarios, you may want to rely on best-fit parsing by setting the `strict_parsing` parameter to `False`.
198+
199+
Note that best-fit parsing is designed to be similar to how web browsers would handle non-standard or broken HTML.
161200

162201
=== "components.py"
163202

@@ -166,9 +205,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
166205
from django.http import HttpResponse
167206
from django_idom.components import view_to_component
168207

169-
@view_to_component(compatibility=True)
208+
@view_to_component(strict_parsing=False)
170209
def hello_world_view(request):
171-
return HttpResponse("Hello World!")
210+
return HttpResponse("<my-tag> Hello World </my-tag>")
172211

173212
@component
174213
def my_component():
@@ -177,13 +216,17 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
177216
)
178217
```
179218

180-
??? question "What is `strict_parsing`?"
181219

182-
By default, an exception will be generated if your view's HTML does not perfectly adhere to HTML5.
183220

184-
However, there are some circumstances where you may not have control over the original HTML, so you may be unable to fix it. Or you may be relying on non-standard HTML tags such as `#!html <my-tag> Hello World </my-tag>`.
221+
---
185222

186-
In these scenarios, you may want to rely on best-fit parsing by setting the `strict_parsing` parameter to `False`.
223+
<font size="4">**`compatibility`**</font>
224+
225+
For views that rely on HTTP responses other than `GET` (such as `PUT`, `POST`, `PATCH`, etc), you should consider using compatibility mode to render your view within an iframe.
226+
227+
Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments do not apply to compatibility mode.
228+
229+
Please note that by default the iframe is unstyled, and thus won't look pretty until you add some CSS.
187230

188231
=== "components.py"
189232

@@ -192,9 +235,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
192235
from django.http import HttpResponse
193236
from django_idom.components import view_to_component
194237

195-
@view_to_component(strict_parsing=False)
238+
@view_to_component(compatibility=True)
196239
def hello_world_view(request):
197-
return HttpResponse("<my-tag> Hello World </my-tag>")
240+
return HttpResponse("Hello World!")
198241

199242
@component
200243
def my_component():
@@ -203,9 +246,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
203246
)
204247
```
205248

206-
Note that best-fit parsing is very similar to how web browsers will handle broken HTML.
249+
---
207250

208-
??? question "What is `transforms`?"
251+
<font size="4">**`transforms`**</font>
209252

210253
After your view has been turned into [VDOM](https://idom-docs.herokuapp.com/docs/reference/specifications.html#vdom) (python dictionaries), `view_to_component` will call your `transforms` functions on every VDOM node.
211254

docs/src/features/hooks.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,6 @@ You can expect this hook to provide strings such as `/idom/my_path`.
324324
return html.div(my_location)
325325
```
326326

327-
??? info "This hook's behavior will be changed in a future update"
328-
329-
This hook will be updated to return the browser's currently active path. This change will come in alongside IDOM URL routing support.
330-
331-
Check out [idom-team/idom-router#2](https://github.com/idom-team/idom-router/issues/2) for more information.
332-
333327
??? example "See Interface"
334328

335329
<font size="4">**Parameters**</font>
@@ -342,6 +336,12 @@ You can expect this hook to provide strings such as `/idom/my_path`.
342336
| --- | --- |
343337
| `Location` | A object containing the current URL's `pathname` and `search` query. |
344338

339+
??? info "This hook's behavior will be changed in a future update"
340+
341+
This hook will be updated to return the browser's currently active path. This change will come in alongside IDOM URL routing support.
342+
343+
Check out [idom-team/idom-router#2](https://github.com/idom-team/idom-router/issues/2) for more information.
344+
345345
## Use Origin
346346

347347
This is a shortcut that returns the Websocket's `origin`.

docs/src/getting-started/learn-more.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ Additionally, the vast majority of tutorials/guides you find for React can be ap
88

99
| Learn More |
1010
| --- |
11-
| [Django-IDOM Advanced Usage](../features/components.md){ .md-button } [IDOM Hooks, Events, and More](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html){ .md-button } [Ask Questions on GitHub Discussions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } |
11+
| [Django-IDOM Advanced Usage](../features/components.md){ .md-button } [IDOM Core Documentation](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html){ .md-button } [Ask Questions on GitHub Discussions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } |

docs/src/installation/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ In your settings you'll need to add `django_idom` to [`INSTALLED_APPS`](https://
2929

3030
Django-IDOM requires ASGI in order to use Websockets.
3131

32-
If you haven't enabled ASGI on your **Django project** yet, you'll need to add `channels` to `INSTALLED_APPS` and set your `ASGI_APPLICATION` variable.
32+
If you haven't enabled ASGI on your **Django project** yet, you'll need to install `channels[daphne]`, add `daphne` to `INSTALLED_APPS`, then set your `ASGI_APPLICATION` variable.
3333

3434
Read the [Django Channels Docs](https://channels.readthedocs.io/en/stable/installation.html) for more info.
3535

3636
=== "settings.py"
3737

3838
```python linenums="1"
3939
INSTALLED_APPS = [
40-
"channels",
40+
"daphne",
4141
...
4242
]
4343
ASGI_APPLICATION = "example_project.asgi.application"

noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test_suite(session: Session) -> None:
6363
def test_types(session: Session) -> None:
6464
install_requirements_file(session, "check-types")
6565
install_requirements_file(session, "pkg-deps")
66-
session.run("mypy", "--show-error-codes", "src/django_idom")
66+
session.run("mypy", "--show-error-codes", "src/django_idom", "tests/test_app")
6767

6868

6969
@nox.session

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ ignore_missing_imports = true
1616
warn_unused_configs = true
1717
warn_redundant_casts = true
1818
warn_unused_ignores = true
19+
check_untyped_defs = true

requirements/pkg-deps.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
channels >=3.0.0
1+
channels >=4.0.0
22
idom >=0.40.2, <0.41.0
33
aiofile >=3.0
44
typing_extensions

src/django_idom/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django_idom.websocket.paths import IDOM_WEBSOCKET_PATH
44

55

6-
__version__ = "2.0.1"
6+
__version__ = "2.1.0"
77
__all__ = [
88
"IDOM_WEBSOCKET_PATH",
99
"IdomWebsocket",

0 commit comments

Comments
 (0)