|
2 | 2 |
|
3 | 3 | import json
|
4 | 4 | import os
|
5 |
| -from pathlib import Path |
6 | 5 | from typing import TYPE_CHECKING, Any, Callable, Union, cast
|
7 | 6 | from urllib.parse import urlencode
|
8 | 7 | from uuid import uuid4
|
9 | 8 |
|
10 | 9 | from django.contrib.staticfiles.finders import find
|
11 | 10 | from django.core.cache import caches
|
12 |
| -from django.forms import BooleanField, ChoiceField, Form, MultipleChoiceField |
13 | 11 | from django.http import HttpRequest
|
14 | 12 | from django.urls import reverse
|
15 | 13 | from reactpy import component, hooks, html, utils
|
16 | 14 | from reactpy.types import ComponentType, Key, VdomDict
|
17 |
| -from reactpy.web import export, module_from_file |
18 | 15 |
|
19 | 16 | from reactpy_django.exceptions import ViewNotRegisteredError
|
| 17 | +from reactpy_django.forms.components import _django_form |
20 | 18 | from reactpy_django.html import pyscript
|
21 |
| -from reactpy_django.transforms import ( |
22 |
| - convert_option_props, |
23 |
| - convert_textarea_children_to_prop, |
24 |
| - ensure_controlled_inputs, |
25 |
| - standardize_prop_names, |
26 |
| -) |
27 | 19 | from reactpy_django.utils import (
|
28 | 20 | generate_obj_name,
|
29 | 21 | import_module,
|
|
35 | 27 | if TYPE_CHECKING:
|
36 | 28 | from collections.abc import Sequence
|
37 | 29 |
|
| 30 | + from django.forms import Form |
38 | 31 | from django.views import View
|
39 | 32 |
|
40 |
| -DjangoForm = export( |
41 |
| - module_from_file("reactpy-django", file=Path(__file__).parent / "static" / "reactpy_django" / "client.js"), |
42 |
| - ("DjangoForm"), |
43 |
| -) |
44 |
| - |
45 | 33 |
|
46 | 34 | def view_to_component(
|
47 | 35 | view: Callable | View | str,
|
@@ -263,104 +251,6 @@ def _django_js(static_path: str):
|
263 | 251 | return html.script(_cached_static_contents(static_path))
|
264 | 252 |
|
265 | 253 |
|
266 |
| -@component |
267 |
| -def _django_form( |
268 |
| - form: type[Form], top_children: Sequence, bottom_children: Sequence, auto_submit: bool, auto_submit_wait: int |
269 |
| -): |
270 |
| - # TODO: Implement form restoration on page reload. Probably want to create a new setting called |
271 |
| - # form_restoration_method that can be set to "URL", "CLIENT_STORAGE", "SERVER_SESSION", or None. |
272 |
| - # Or maybe just recommend pre-rendering to have the browser handle it. |
273 |
| - # Be clear that URL mode will limit you to one form per page. |
274 |
| - # TODO: Test this with django-bootstrap forms and see how errors behave |
275 |
| - # TODO: Test this with django-colorfield and django-ace |
276 |
| - # TODO: Add pre-submit and post-submit hooks |
277 |
| - # TODO: Add auto-save option for database-backed forms |
278 |
| - uuid_ref = hooks.use_ref(uuid4().hex.replace("-", "")) |
279 |
| - top_children_count = hooks.use_ref(len(top_children)) |
280 |
| - bottom_children_count = hooks.use_ref(len(bottom_children)) |
281 |
| - submitted_data, set_submitted_data = hooks.use_state({} or None) |
282 |
| - |
283 |
| - uuid = uuid_ref.current |
284 |
| - |
285 |
| - # Don't allow the count of top and bottom children to change |
286 |
| - if len(top_children) != top_children_count.current or len(bottom_children) != bottom_children_count.current: |
287 |
| - msg = "Dynamically changing the number of top or bottom children is not allowed." |
288 |
| - raise ValueError(msg) |
289 |
| - |
290 |
| - # Try to initialize the form with the provided data |
291 |
| - try: |
292 |
| - initialized_form = form(data=submitted_data) |
293 |
| - except Exception as e: |
294 |
| - if not isinstance(form, type(Form)): |
295 |
| - msg = ( |
296 |
| - "The provided form must be an uninitialized Django Form. " |
297 |
| - "Do NOT initialize your form by calling it (ex. `MyForm()`)." |
298 |
| - ) |
299 |
| - raise TypeError(msg) from e |
300 |
| - raise |
301 |
| - |
302 |
| - # Run the form validation, if data was provided |
303 |
| - if submitted_data: |
304 |
| - initialized_form.full_clean() |
305 |
| - |
306 |
| - def on_submit_callback(new_data: dict[str, Any]): |
307 |
| - choice_field_map = { |
308 |
| - field_name: {choice_value: choice_key for choice_key, choice_value in field.choices} |
309 |
| - for field_name, field in initialized_form.fields.items() |
310 |
| - if isinstance(field, ChoiceField) |
311 |
| - } |
312 |
| - multi_choice_fields = { |
313 |
| - field_name |
314 |
| - for field_name, field in initialized_form.fields.items() |
315 |
| - if isinstance(field, MultipleChoiceField) |
316 |
| - } |
317 |
| - boolean_fields = { |
318 |
| - field_name for field_name, field in initialized_form.fields.items() if isinstance(field, BooleanField) |
319 |
| - } |
320 |
| - |
321 |
| - # Choice fields submit their values as text, but Django choice keys are not always equal to their values. |
322 |
| - # Due to this, we need to convert the text into keys that Django would be happy with |
323 |
| - for choice_field_name, choice_map in choice_field_map.items(): |
324 |
| - if choice_field_name in new_data: |
325 |
| - submitted_value = new_data[choice_field_name] |
326 |
| - if isinstance(submitted_value, list): |
327 |
| - new_data[choice_field_name] = [ |
328 |
| - choice_map.get(submitted_value_item, submitted_value_item) |
329 |
| - for submitted_value_item in submitted_value |
330 |
| - ] |
331 |
| - elif choice_field_name in multi_choice_fields: |
332 |
| - new_data[choice_field_name] = [choice_map.get(submitted_value, submitted_value)] |
333 |
| - else: |
334 |
| - new_data[choice_field_name] = choice_map.get(submitted_value, submitted_value) |
335 |
| - |
336 |
| - # Convert boolean field text into actual booleans |
337 |
| - for boolean_field_name in boolean_fields: |
338 |
| - new_data[boolean_field_name] = boolean_field_name in new_data |
339 |
| - |
340 |
| - # TODO: ReactPy's use_state hook really should be de-duplicating this by itself. Needs upstream fix. |
341 |
| - if submitted_data != new_data: |
342 |
| - set_submitted_data(new_data) |
343 |
| - |
344 |
| - async def on_change(event): ... |
345 |
| - |
346 |
| - rendered_form = utils.html_to_vdom( |
347 |
| - initialized_form.render(), |
348 |
| - standardize_prop_names, |
349 |
| - convert_textarea_children_to_prop, |
350 |
| - convert_option_props, |
351 |
| - ensure_controlled_inputs(on_change), |
352 |
| - strict=False, |
353 |
| - ) |
354 |
| - |
355 |
| - return html.form( |
356 |
| - {"id": f"reactpy-{uuid}"}, |
357 |
| - DjangoForm({"onSubmitCallback": on_submit_callback, "formId": f"reactpy-{uuid}"}), |
358 |
| - *top_children, |
359 |
| - html.div({"key": uuid4().hex}, rendered_form), |
360 |
| - *bottom_children, |
361 |
| - ) |
362 |
| - |
363 |
| - |
364 | 254 | def _cached_static_contents(static_path: str) -> str:
|
365 | 255 | from reactpy_django.config import REACTPY_CACHE
|
366 | 256 |
|
|
0 commit comments