5
5
from uuid import uuid4
6
6
7
7
from channels .db import database_sync_to_async
8
- from django .forms import Form
8
+ from django .forms import Form , ModelForm
9
9
from reactpy import component , hooks , html , utils
10
10
from reactpy .core .events import event
11
11
from reactpy .web import export , module_from_file
31
31
32
32
@component
33
33
def _django_form (
34
- form : type [Form ],
34
+ form : type [Form | ModelForm ],
35
35
extra_props : dict ,
36
36
on_success : Callable [[FormEvent ], None ] | None ,
37
37
on_error : Callable [[FormEvent ], None ] | None ,
38
38
on_submit : Callable [[FormEvent ], None ] | None ,
39
39
on_change : Callable [[FormEvent ], None ] | None ,
40
+ auto_save : bool ,
40
41
form_template : str | None ,
41
42
top_children : Sequence ,
42
43
bottom_children : Sequence ,
@@ -46,7 +47,6 @@ def _django_form(
46
47
# Perhaps pre-rendering is robust enough already handle this scenario?
47
48
# Additionally, "URL" mode would limit the user to one form per page.
48
49
# TODO: Test this with django-colorfield, django-ace, django-crispy-forms
49
- # TODO: Add auto-save option for database-backed forms
50
50
uuid_ref = hooks .use_ref (uuid4 ().hex .replace ("-" , "" ))
51
51
top_children_count = hooks .use_ref (len (top_children ))
52
52
bottom_children_count = hooks .use_ref (len (bottom_children ))
@@ -59,20 +59,17 @@ def _django_form(
59
59
msg = "Dynamically changing the number of top or bottom children is not allowed."
60
60
raise ValueError (msg )
61
61
62
+ # Ensure the provided form is a Django Form
63
+ if not isinstance (form , (type (Form ), type (ModelForm ))):
64
+ msg = (
65
+ "The provided form must be an uninitialized Django Form. "
66
+ "Do NOT initialize your form by calling it (ex. `MyForm()`)."
67
+ )
68
+ raise TypeError (msg )
69
+
62
70
# Try to initialize the form with the provided data
63
- try :
64
- initialized_form = form (data = submitted_data )
65
- except Exception as e :
66
- if not isinstance (form , type (Form )):
67
- msg = (
68
- "The provided form must be an uninitialized Django Form. "
69
- "Do NOT initialize your form by calling it (ex. `MyForm()`)."
70
- )
71
- raise TypeError (msg ) from e
72
- raise
73
-
74
- # Set up the form event object
75
- form_event = FormEvent (form = initialized_form , data = submitted_data or {})
71
+ initialized_form = form (data = submitted_data )
72
+ form_event = FormEvent (form = initialized_form , data = submitted_data or {}, set_data = set_submitted_data )
76
73
77
74
# Validate and render the form
78
75
@hooks .use_effect
@@ -85,6 +82,9 @@ async def render_form():
85
82
on_success (form_event )
86
83
if not success and on_error :
87
84
on_error (form_event )
85
+ if success and auto_save and isinstance (initialized_form , ModelForm ):
86
+ await database_sync_to_async (initialized_form .save )()
87
+ set_submitted_data (None )
88
88
89
89
set_rendered_form (await database_sync_to_async (initialized_form .render )(form_template ))
90
90
@@ -99,7 +99,7 @@ def on_submit_callback(new_data: dict[str, Any]):
99
99
convert_boolean_fields (new_data , initialized_form )
100
100
101
101
if on_submit :
102
- on_submit (FormEvent (form = initialized_form , data = new_data ))
102
+ on_submit (FormEvent (form = initialized_form , data = new_data , set_data = set_submitted_data ))
103
103
104
104
# TODO: The `use_state`` hook really should be de-duplicating this by itself. Needs upstream fix.
105
105
if submitted_data != new_data :
0 commit comments