Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
46b4856
Some new extension points
rgs258 Sep 12, 2020
1acf5be
Extension point for create_field_class and some comments
rgs258 Sep 14, 2020
9068bb0
Encapsulates the field_name creation such that there is a method to o…
rgs258 Oct 16, 2020
e893e7d
Undo AbstractFormOut - no longer needed
rgs258 Nov 18, 2020
2104faf
Merge branch 'master' into wrds_forms_work
rgs258 Nov 18, 2020
8f45527
Documentation Improvements
rgs258 Nov 19, 2020
5cb1022
Upgrade to wagtail 2.12 and fix a bug
rgs258 Mar 16, 2021
6527012
Merge remote-tracking branch 'remotes/gasman/wagtail-2.12-compat' int…
rgs258 Mar 18, 2021
9a11923
Wagtail 2.13
rgs258 May 19, 2021
969aeaa
Merge remote-tracking branch 'remotes/origin/master' into wrds_forms_…
rgs258 May 26, 2021
3384f7c
Allow instantiate_block method to return None in order to cause a blo…
rgs258 May 26, 2021
1468b9c
Merge remote-tracking branch 'remotes/origin/master' into wrds_forms_…
rgs258 Jan 7, 2022
533b347
Merge pull request #5 from FlipperPA/upgrade/wagtail-2.15
rgs258 Jan 7, 2022
2ba72d1
Merge branch 'labd:master' into wrds_forms_work
rgs258 Mar 16, 2022
f7a0493
Merge branch 'wagtail_216' into wrds_forms_work
rgs258 Mar 16, 2022
940e4ff
Merge remote-tracking branch 'remotes/origin/master' into wrds_forms_…
rgs258 May 10, 2022
4ab78fe
Merge branch 'labd:master' into wrds_forms_work
rgs258 May 11, 2022
401ecd0
clean(lint): remove unused import
Sep 23, 2022
2d011a2
fix(ci): enable testing for WT 3 and 4
Sep 23, 2022
d6339a5
fix(example app): Have example app work, issues w/ outdated db and un…
Sep 23, 2022
8eca002
fix(deps) allow installing w/ version 3.0.3 of WT
Sep 23, 2022
a6beb22
Merge branch 'labd:master' into wrds_forms_work
rgs258 Oct 18, 2022
820dab7
Merge pull request #8 from TonisPiip/wagtail-3
rgs258 Oct 18, 2022
d07911d
going after wagtail4
rgs258 Oct 19, 2022
883fc2b
Require Wagtail 3 or higher for package path changes
rgs258 Oct 25, 2022
66d8402
Removes StreamFieldPanel and adds use_json_field=True to instantiatio…
rgs258 Oct 25, 2022
0bdc091
allow wagtail 4.1
rgs258 Nov 14, 2022
e7a0df0
Merge branch 'master' into wrds_forms_work
rgs258 Apr 4, 2023
e1214f2
Adds get_local_blocks to allow for overriding the local_blocks to be …
rgs258 Dec 19, 2023
9117bbb
Merge branch 'master' into wrds_forms_work
rgs258 Dec 19, 2023
bfb9542
Adds yet another extension point, `get_form_block_class`, to allow th…
rgs258 Jan 5, 2024
f132758
Adjusts wagtailstreamforms/wagtailstreamforms_fields.py to use new se…
rgs258 Jan 31, 2024
891b666
Adds yet another override point: `get_form_block_kwargs`.
rgs258 Jan 31, 2024
170a947
Compatible Wagtail 6.0.1, Django 5.0.2
rense Feb 29, 2024
cb61586
Fix example project
rense Feb 29, 2024
9e95dfd
github workflows
rense Feb 29, 2024
688f47a
Merge pull request #9 from ammohq/master
rgs258 Sep 17, 2024
8e6e4be
Merge branch 'master' into wrds_forms_work
rgs258 Sep 17, 2024
2d5054a
Undo removal of use_json_field=True
rgs258 Sep 17, 2024
575549d
wt6.3
rense Nov 14, 2024
d1a1132
workflow
rense Nov 14, 2024
58c476e
no coverage files
rense Nov 14, 2024
96e69f2
remove generic_chooser
rense Nov 14, 2024
e74e576
icon name
rense Nov 14, 2024
576d549
trying to improve pyproject.toml
rgs258 Jan 29, 2025
ece0806
convert to a hatch project - why not?
rgs258 Jan 29, 2025
313a06e
Merge pull request #10 from ammohq/wt6.3
rgs258 Mar 17, 2025
43f75fc
Merge branch 'master' into wrds_forms_work
rgs258 Mar 17, 2025
84477fc
Update wagtailstreamforms dependency requirements for Wagtail 6 compa…
rgs258 Mar 25, 2025
cc10c18
Merge remote-tracking branch 'labd/master'
rgs258 Sep 26, 2025
347710d
match labd master
rgs258 Sep 26, 2025
9807fc7
Merge branch 'master' into wrds_forms_work
rgs258 Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions example/wagtailstreamforms_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,25 @@ def get_options(self, block_value):
options.update({"required": True})
return options

def get_form_block_class(self):
return blocks.StructBlock

def get_local_blocks(self):
return [
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
]

def get_form_block_kwargs(self):
return {
"icon": self.icon,
"label": self.label,
}

def get_form_block(self):
return blocks.StructBlock(
[
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
],
icon=self.icon,
label=self.label,
return self.get_form_block_class()(
self.get_local_blocks(),
**self.get_form_block_kwargs(),
)


Expand All @@ -49,18 +60,20 @@ def get_regex_choices(self):
("^[a-zA-Z0-9]+$", "Letters and numbers only"),
)

def get_local_blocks(self):
return [
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
("regex", blocks.ChoiceBlock(choices=self.get_regex_choices())),
("error_message", blocks.CharBlock()),
("default_value", blocks.CharBlock(required=False)),
]

def get_form_block(self):
return blocks.StructBlock(
[
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
("regex", blocks.ChoiceBlock(choices=self.get_regex_choices())),
("error_message", blocks.CharBlock()),
("default_value", blocks.CharBlock(required=False)),
],
icon=self.icon,
label=self.label,
return self.get_form_block_class()(
self.get_local_blocks(),
**self.get_form_block_kwargs()
)


Expand All @@ -79,13 +92,15 @@ def get_options(self, block_value):
options.update({"queryset": self.get_queryset()})
return options

def get_local_blocks(self):
return [
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
]

def get_form_block(self):
return blocks.StructBlock(
[
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
],
icon=self.icon,
label=self.label,
return self.get_form_block_class()(
self.get_local_blocks(),
**self.get_form_block_kwargs()
)
61 changes: 48 additions & 13 deletions wagtailstreamforms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class SingleLineTextField(BaseField):
"""

if cls is None:

def decorator(cls):
register(field_name, cls)
return cls
Expand Down Expand Up @@ -147,24 +146,60 @@ def get_options(self, block_value):
"initial": self.get_formfield_initial(block_value),
}

def get_form_block_class(self):
"""
The StreamField block class to be created for this field. This is
almost always a StructBlock, but conceptually it could be any structural block.

Override this method and return a subclass of a structural block for further
control over the block class, such as overriding the clean() method to provide
custom validation.
:return: The ``wagtail.blocks.StructBlock`` to be used in the StreamField
"""
return blocks.StructBlock

def get_form_block_kwargs(self):
"""The kwargs to be passed into the StreamField block class.

Override this to provide additional kwargs to the StreamField block class.

:return: The kwargs to be passed into the StreamField block class
"""
return {
"icon": self.icon,
"label": self.label,
}

def get_form_block(self):
"""The StreamField StructBlock.
"""The StreamField block class.

Override this to provide additional fields in the StreamField.

:return: The ``wagtail.core.blocks.StructBlock`` to be used in the StreamField
"""
return blocks.StructBlock(
[
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
("default_value", blocks.CharBlock(required=False)),
],
icon=self.icon,
label=self.label,
:return: The resuld of calling get_form_block_class() is to be used in the
StreamField
"""
return self.get_form_block_class()(
self.get_local_blocks(),
**self.get_form_block_kwargs(),
)

def get_local_blocks(self):
"""The blocks that should be added to the StructBlock for this field.

Override this to add blocks to, or remove blocks from, the StructBlock
before it is instantiated. This is useful because adding blocks to the
StructBlock after instantiation requires mucking with the StructBlock's
internal, undocumented API.

:return: A list of tuples containing the block name and block instance.
"""
return [
("label", blocks.CharBlock()),
("help_text", blocks.CharBlock(required=False)),
("required", blocks.BooleanBlock(required=False)),
("default_value", blocks.CharBlock(required=False)),
]


class HookMultiSelectFormField(forms.MultipleChoiceField):
widget = forms.CheckboxSelectMultiple
Expand Down
53 changes: 40 additions & 13 deletions wagtailstreamforms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,8 @@ def formfields(self):

formfields = OrderedDict()

registered_fields = get_fields()

for field in self.fields:
field_type = field.get("type")
field_value = field.get("value")

# check we have the field
if field_type not in registered_fields:
raise AttributeError("Could not find a registered field of type %s" % field_type)

# get the field
registered_cls = registered_fields[field_type]()
field_name = registered_cls.get_formfield_name(field_value)
field_cls = registered_cls.get_formfield(field_value)
field_name, field_cls = self.create_field_class(field)
formfields[field_name] = field_cls

# add fields to uniquely identify the form
Expand All @@ -48,6 +36,45 @@ def formfields(self):

return formfields

def create_field_class(self, field):
"""
Encapsulates the field_cls creation such that there is a method to override
when the field_cls needs to be modified.

:param field: StreamBlock representing a form field; an item in
fields.stream_data
:return: a tuple of field_name - the name to use in the html form for this
field, and field_cls - in instantiated field class that may be added to a form
"""
registered_fields = get_fields()

field_type = field.get("type")
field_value = field.get("value")
# check we have the field
if field_type not in registered_fields:
raise AttributeError(
"Could not find a registered field of type %s" % field_type
)

# get the field
registered_cls = registered_fields[field_type]()
field_cls = registered_cls.get_formfield(field_value)
field_name = self.create_field_name(registered_cls, field)
return field_name, field_cls

def create_field_name(self, registered_cls, field):
"""
Encapsulates the field_name creation such that there is a method to override
when the field_name needs to be modified.

:param field: StreamBlock representing a form field; an item in
fields.stream_data
:param registered_cls: The subclass of wagtailstreamforms.fields.BaseField
that defined this form field
:return: a name to use in the html form for this field
"""
return registered_cls.get_formfield_name(field.get("value"))

def get_form_class(self):
return type(str("StreamformsForm"), (BaseForm,), self.formfields)

Expand Down
20 changes: 14 additions & 6 deletions wagtailstreamforms/streamfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ def __init__(self, local_blocks=None, **kwargs) -> None:
"'%s' must be a subclass of '%s'" % (field_class, BaseField)
)

# assign the block
block = field_class().get_form_block()
block.set_name(name)
self._child_blocks[name] = block
# assign the block if instantiation returns non None
if block := self.instantiate_block(field_class, name):
self._child_blocks[name] = block

self._dependencies = self._child_blocks.values()

def instantiate_block(self, field_class, name):
"""
Provides an extension point for changing attributes of blocks, like the
meta.group
"""
block = field_class().get_form_block()
block.set_name(name)
return block

@property
def child_blocks(self):
return self._child_blocks
Expand All @@ -43,6 +51,6 @@ def dependencies(self):


class FormFieldsStreamField(StreamField):
def __init__(self, block_types, **kwargs) -> None:
super().__init__(block_types, **kwargs)
def __init__(self, block_types, use_json_field=None, **kwargs):
super().__init__(block_types, use_json_field=use_json_field, **kwargs)
self.stream_block = FormFieldStreamBlock(block_types, required=not self.blank)
Loading