We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Select2 Tag widget error when using on empty event form
Perhaps a related issue - I also got a 500 Server Error when trying to accept a workshop request (e.g. https://amy.carpentries.org/requests/selforganised_submission/552/) but then on repeated attempts accepting some of the problematic requests worked so not sure what was happening but thought I should report it. This one is still causing problems: https://amy.carpentries.org/requests/selforganised_submission/552/

_Originally posted by @anenadic in https://github.com/carpentries/amy/issues/1650#issuecomment-623344963_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `amy/workshops/fields.py`
Content:
```
1 from django_select2.forms import (
2     Select2Widget as DS2_Select2Widget,
3     Select2MultipleWidget as DS2_Select2MultipleWidget,
4     ModelSelect2Widget as DS2_ModelSelect2Widget,
5     ModelSelect2MultipleWidget as DS2_ModelSelect2MultipleWidget,
6     Select2TagWidget as DS2_Select2TagWidget,
7 )
8 from django.core.validators import RegexValidator, MaxLengthValidator
9 from django.db import models
10 from django import forms
11 from django.utils.safestring import mark_safe
12 
13 
14 GHUSERNAME_MAX_LENGTH_VALIDATOR = MaxLengthValidator(39,
15     message='Maximum allowed username length is 39 characters.',
16 )
17 # according to https://stackoverflow.com/q/30281026,
18 # GH username can only contain alphanumeric characters and
19 # hyphens (but not consecutive), cannot start or end with
20 # a hyphen, and can't be longer than 39 characters
21 GHUSERNAME_REGEX_VALIDATOR = RegexValidator(
22     # regex inspired by above StackOverflow thread
23     regex=r'^([a-zA-Z\d](?:-?[a-zA-Z\d])*)$',
24     message='This is not a valid GitHub username.',
25 )
26 
27 
28 class NullableGithubUsernameField(models.CharField):
29     def __init__(self, **kwargs):
30         kwargs.setdefault('null', True)
31         kwargs.setdefault('blank', True)
32         kwargs.setdefault('default', '')
33         # max length of the GH username is 39 characters
34         kwargs.setdefault('max_length', 39)
35         super().__init__(**kwargs)
36 
37     default_validators = [
38         GHUSERNAME_MAX_LENGTH_VALIDATOR,
39         GHUSERNAME_REGEX_VALIDATOR,
40     ]
41 
42 
43 #------------------------------------------------------------
44 
45 class FakeRequiredMixin:
46     def __init__(self, *args, **kwargs):
47         # Intercept "fake_required" attribute that's used for marking field
48         # with "*" (asterisk) even though it's not required.
49         # Additionally `fake_required` doesn't trigger any validation.
50         self.fake_required = kwargs.pop('fake_required', False)
51         super().__init__(*args, **kwargs)
52 
53 
54 class RadioSelectWithOther(FakeRequiredMixin, forms.RadioSelect):
55     """A RadioSelect widget that should render additional field ('Other').
56 
57     We have a number of occurences of two model fields bound together: one
58     containing predefined set of choices, the other being a text input for
59     other input user wants to choose instead of one of our predefined options.
60 
61     This widget should help with rendering two widgets in one table row."""
62 
63     other_field = None  # to be bound later
64 
65     def __init__(self, other_field_name, *args, **kwargs):
66         super().__init__(*args, **kwargs)
67         self.other_field_name = other_field_name
68 
69 
70 class CheckboxSelectMultipleWithOthers(FakeRequiredMixin, forms.CheckboxSelectMultiple):
71     """A multiple choice widget that should render additional field ('Other').
72 
73     We have a number of occurences of two model fields bound together: one
74     containing predefined set of choices, the other being a text input for
75     other input user wants to choose instead of one of our predefined options.
76 
77     This widget should help with rendering two widgets in one table row."""
78 
79     other_field = None  # to be bound later
80 
81     def __init__(self, other_field_name, *args, **kwargs):
82         super().__init__(*args, **kwargs)
83         self.other_field_name = other_field_name
84 
85 
86 class RadioSelectFakeMultiple(FakeRequiredMixin, forms.RadioSelect):
87     """Pretend to be a radio-select with multiple selection possible. This
88     is intended to 'fool' Django into thinking that user selected 1 item on
89     a multi-select item list."""
90     allow_multiple_selected = True
91 
92 
93 class SafeLabelFromInstanceMixin:
94     def label_from_instance(self, obj):
95         return mark_safe(obj)
96 
97 
98 class SafeModelChoiceField(SafeLabelFromInstanceMixin, forms.ModelChoiceField):
99     pass
100 
101 
102 class SafeModelMultipleChoiceField(SafeLabelFromInstanceMixin,
103                                    forms.ModelMultipleChoiceField):
104     pass
105 
106 
107 class CurriculumModelMultipleChoiceField(SafeModelMultipleChoiceField):
108     def label_from_instance(self, obj):
109         # Display in tooltip (it's a little better than popover, because it
110         # auto-hides and doesn't require clicking on the element, whereas
111         # popover by clicking will automatically select the clicked item)
112         data = (
113             '<a tabindex="0" role="button" data-toggle="tooltip" '
114             'data-placement="top" title="{description}">{obj}</a>'
115             .format(obj=obj, description=obj.description)
116         )
117         return super().label_from_instance(data)
118 
119 
120 #------------------------------------------------------------
121 
122 class Select2BootstrapMixin:
123     def build_attrs(self, *args, **kwargs):
124         attrs = super().build_attrs(*args, **kwargs)
125         attrs.setdefault('data-theme', 'bootstrap4')
126         return attrs
127 
128 
129 class Select2NoMinimumInputLength:
130     def build_attrs(self, *args, **kwargs):
131         # Let's set up the minimum input length first!
132         # It will overwrite `setdefault('data-minimum-input-length')` from
133         # other mixins.
134         self.attrs.setdefault('data-minimum-input-length', 0)
135         attrs = super().build_attrs(*args, **kwargs)
136         return attrs
137 
138 
139 
140 class Select2Widget(FakeRequiredMixin, Select2BootstrapMixin,
141                     DS2_Select2Widget):
142     pass
143 
144 
145 class Select2MultipleWidget(Select2BootstrapMixin, DS2_Select2MultipleWidget):
146     pass
147 
148 
149 class ModelSelect2Widget(Select2BootstrapMixin, Select2NoMinimumInputLength,
150                          DS2_ModelSelect2Widget):
151     pass
152 
153 
154 class ModelSelect2MultipleWidget(Select2BootstrapMixin,
155                                  Select2NoMinimumInputLength,
156                                  DS2_ModelSelect2MultipleWidget):
157     pass
158 
159 
160 TAG_SEPARATOR = ';'
161 
162 
163 class Select2TagWidget(Select2BootstrapMixin, DS2_Select2TagWidget):
164     def build_attrs(self, base_attrs, extra_attrs=None):
165         """Select2's tag attributes. By default other token separators are
166         used, but we want to use "," and ";"."""
167         default_attrs = {
168             'data-minimum-input-length': 1,
169             'data-tags': 'true',
170             'data-token-separators': '[",", ";"]'
171         }
172         assert TAG_SEPARATOR in default_attrs['data-token-separators']
173 
174         default_attrs.update(base_attrs)
175         return super().build_attrs(default_attrs, extra_attrs=extra_attrs)
176 
177     def value_from_datadict(self, data, files, name):
178         # sometimes data is held as an immutable QueryDict
179         # in those cases, we need to make a copy of it to "disable"
180         # the mutability
181         try:
182             data_mutable = data.copy()
183         except AttributeError:
184             data_mutable = data
185 
186         data_mutable.setdefault(name, '')
187         values = super().value_from_datadict(data_mutable, files, name)
188         return TAG_SEPARATOR.join(values)
189 
190     def optgroups(self, name, value, attrs=None):
191         """Example from
192         https://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget"""
193         values = value[0].split(TAG_SEPARATOR) if value[0] else []
194         selected = set(values)
195         subgroup = [
196             self.create_option(name, v, v, selected, i)
197             for i, v in enumerate(values)
198         ]
199         return [(None, subgroup, 0)]
200
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/amy/workshops/fields.py b/amy/workshops/fields.py
--- a/amy/workshops/fields.py
+++ b/amy/workshops/fields.py
@@ -190,7 +190,11 @@
     def optgroups(self, name, value, attrs=None):
         """Example from
         https://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget"""
-        values = value[0].split(TAG_SEPARATOR) if value[0] else []
+        try:
+            values = value[0].split(TAG_SEPARATOR)
+        except (IndexError, AttributeError):
+            values = []
+
         selected = set(values)
         subgroup = [
             self.create_option(name, v, v, selected, i)

{"golden_diff": "diff --git a/amy/workshops/fields.py b/amy/workshops/fields.py\n--- a/amy/workshops/fields.py\n+++ b/amy/workshops/fields.py\n@@ -190,7 +190,11 @@\n     def optgroups(self, name, value, attrs=None):\n         \"\"\"Example from\n         https://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget\"\"\"\n-        values = value[0].split(TAG_SEPARATOR) if value[0] else []\n+        try:\n+            values = value[0].split(TAG_SEPARATOR)\n+        except (IndexError, AttributeError):\n+            values = []\n+\n         selected = set(values)\n         subgroup = [\n             self.create_option(name, v, v, selected, i)\n", "issue": "Select2 Tag widget error when using on empty event form\nPerhaps a related issue - I also got a 500 Server Error when trying to accept a workshop request (e.g. https://amy.carpentries.org/requests/selforganised_submission/552/) but then on repeated attempts accepting some of the problematic requests worked so not sure what was happening but thought I should report it. This one is still causing problems: https://amy.carpentries.org/requests/selforganised_submission/552/\r\n\r\n_Originally posted by @anenadic in https://github.com/carpentries/amy/issues/1650#issuecomment-623344963_\n", "before_files": [{"content": "from django_select2.forms import (\n    Select2Widget as DS2_Select2Widget,\n    Select2MultipleWidget as DS2_Select2MultipleWidget,\n    ModelSelect2Widget as DS2_ModelSelect2Widget,\n    ModelSelect2MultipleWidget as DS2_ModelSelect2MultipleWidget,\n    Select2TagWidget as DS2_Select2TagWidget,\n)\nfrom django.core.validators import RegexValidator, MaxLengthValidator\nfrom django.db import models\nfrom django import forms\nfrom django.utils.safestring import mark_safe\n\n\nGHUSERNAME_MAX_LENGTH_VALIDATOR = MaxLengthValidator(39,\n    message='Maximum allowed username length is 39 characters.',\n)\n# according to https://stackoverflow.com/q/30281026,\n# GH username can only contain alphanumeric characters and\n# hyphens (but not consecutive), cannot start or end with\n# a hyphen, and can't be longer than 39 characters\nGHUSERNAME_REGEX_VALIDATOR = RegexValidator(\n    # regex inspired by above StackOverflow thread\n    regex=r'^([a-zA-Z\\d](?:-?[a-zA-Z\\d])*)$',\n    message='This is not a valid GitHub username.',\n)\n\n\nclass NullableGithubUsernameField(models.CharField):\n    def __init__(self, **kwargs):\n        kwargs.setdefault('null', True)\n        kwargs.setdefault('blank', True)\n        kwargs.setdefault('default', '')\n        # max length of the GH username is 39 characters\n        kwargs.setdefault('max_length', 39)\n        super().__init__(**kwargs)\n\n    default_validators = [\n        GHUSERNAME_MAX_LENGTH_VALIDATOR,\n        GHUSERNAME_REGEX_VALIDATOR,\n    ]\n\n\n#------------------------------------------------------------\n\nclass FakeRequiredMixin:\n    def __init__(self, *args, **kwargs):\n        # Intercept \"fake_required\" attribute that's used for marking field\n        # with \"*\" (asterisk) even though it's not required.\n        # Additionally `fake_required` doesn't trigger any validation.\n        self.fake_required = kwargs.pop('fake_required', False)\n        super().__init__(*args, **kwargs)\n\n\nclass RadioSelectWithOther(FakeRequiredMixin, forms.RadioSelect):\n    \"\"\"A RadioSelect widget that should render additional field ('Other').\n\n    We have a number of occurences of two model fields bound together: one\n    containing predefined set of choices, the other being a text input for\n    other input user wants to choose instead of one of our predefined options.\n\n    This widget should help with rendering two widgets in one table row.\"\"\"\n\n    other_field = None  # to be bound later\n\n    def __init__(self, other_field_name, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.other_field_name = other_field_name\n\n\nclass CheckboxSelectMultipleWithOthers(FakeRequiredMixin, forms.CheckboxSelectMultiple):\n    \"\"\"A multiple choice widget that should render additional field ('Other').\n\n    We have a number of occurences of two model fields bound together: one\n    containing predefined set of choices, the other being a text input for\n    other input user wants to choose instead of one of our predefined options.\n\n    This widget should help with rendering two widgets in one table row.\"\"\"\n\n    other_field = None  # to be bound later\n\n    def __init__(self, other_field_name, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.other_field_name = other_field_name\n\n\nclass RadioSelectFakeMultiple(FakeRequiredMixin, forms.RadioSelect):\n    \"\"\"Pretend to be a radio-select with multiple selection possible. This\n    is intended to 'fool' Django into thinking that user selected 1 item on\n    a multi-select item list.\"\"\"\n    allow_multiple_selected = True\n\n\nclass SafeLabelFromInstanceMixin:\n    def label_from_instance(self, obj):\n        return mark_safe(obj)\n\n\nclass SafeModelChoiceField(SafeLabelFromInstanceMixin, forms.ModelChoiceField):\n    pass\n\n\nclass SafeModelMultipleChoiceField(SafeLabelFromInstanceMixin,\n                                   forms.ModelMultipleChoiceField):\n    pass\n\n\nclass CurriculumModelMultipleChoiceField(SafeModelMultipleChoiceField):\n    def label_from_instance(self, obj):\n        # Display in tooltip (it's a little better than popover, because it\n        # auto-hides and doesn't require clicking on the element, whereas\n        # popover by clicking will automatically select the clicked item)\n        data = (\n            '<a tabindex=\"0\" role=\"button\" data-toggle=\"tooltip\" '\n            'data-placement=\"top\" title=\"{description}\">{obj}</a>'\n            .format(obj=obj, description=obj.description)\n        )\n        return super().label_from_instance(data)\n\n\n#------------------------------------------------------------\n\nclass Select2BootstrapMixin:\n    def build_attrs(self, *args, **kwargs):\n        attrs = super().build_attrs(*args, **kwargs)\n        attrs.setdefault('data-theme', 'bootstrap4')\n        return attrs\n\n\nclass Select2NoMinimumInputLength:\n    def build_attrs(self, *args, **kwargs):\n        # Let's set up the minimum input length first!\n        # It will overwrite `setdefault('data-minimum-input-length')` from\n        # other mixins.\n        self.attrs.setdefault('data-minimum-input-length', 0)\n        attrs = super().build_attrs(*args, **kwargs)\n        return attrs\n\n\n\nclass Select2Widget(FakeRequiredMixin, Select2BootstrapMixin,\n                    DS2_Select2Widget):\n    pass\n\n\nclass Select2MultipleWidget(Select2BootstrapMixin, DS2_Select2MultipleWidget):\n    pass\n\n\nclass ModelSelect2Widget(Select2BootstrapMixin, Select2NoMinimumInputLength,\n                         DS2_ModelSelect2Widget):\n    pass\n\n\nclass ModelSelect2MultipleWidget(Select2BootstrapMixin,\n                                 Select2NoMinimumInputLength,\n                                 DS2_ModelSelect2MultipleWidget):\n    pass\n\n\nTAG_SEPARATOR = ';'\n\n\nclass Select2TagWidget(Select2BootstrapMixin, DS2_Select2TagWidget):\n    def build_attrs(self, base_attrs, extra_attrs=None):\n        \"\"\"Select2's tag attributes. By default other token separators are\n        used, but we want to use \",\" and \";\".\"\"\"\n        default_attrs = {\n            'data-minimum-input-length': 1,\n            'data-tags': 'true',\n            'data-token-separators': '[\",\", \";\"]'\n        }\n        assert TAG_SEPARATOR in default_attrs['data-token-separators']\n\n        default_attrs.update(base_attrs)\n        return super().build_attrs(default_attrs, extra_attrs=extra_attrs)\n\n    def value_from_datadict(self, data, files, name):\n        # sometimes data is held as an immutable QueryDict\n        # in those cases, we need to make a copy of it to \"disable\"\n        # the mutability\n        try:\n            data_mutable = data.copy()\n        except AttributeError:\n            data_mutable = data\n\n        data_mutable.setdefault(name, '')\n        values = super().value_from_datadict(data_mutable, files, name)\n        return TAG_SEPARATOR.join(values)\n\n    def optgroups(self, name, value, attrs=None):\n        \"\"\"Example from\n        https://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget\"\"\"\n        values = value[0].split(TAG_SEPARATOR) if value[0] else []\n        selected = set(values)\n        subgroup = [\n            self.create_option(name, v, v, selected, i)\n            for i, v in enumerate(values)\n        ]\n        return [(None, subgroup, 0)]\n", "path": "amy/workshops/fields.py"}], "after_files": [{"content": "from django_select2.forms import (\n    Select2Widget as DS2_Select2Widget,\n    Select2MultipleWidget as DS2_Select2MultipleWidget,\n    ModelSelect2Widget as DS2_ModelSelect2Widget,\n    ModelSelect2MultipleWidget as DS2_ModelSelect2MultipleWidget,\n    Select2TagWidget as DS2_Select2TagWidget,\n)\nfrom django.core.validators import RegexValidator, MaxLengthValidator\nfrom django.db import models\nfrom django import forms\nfrom django.utils.safestring import mark_safe\n\n\nGHUSERNAME_MAX_LENGTH_VALIDATOR = MaxLengthValidator(39,\n    message='Maximum allowed username length is 39 characters.',\n)\n# according to https://stackoverflow.com/q/30281026,\n# GH username can only contain alphanumeric characters and\n# hyphens (but not consecutive), cannot start or end with\n# a hyphen, and can't be longer than 39 characters\nGHUSERNAME_REGEX_VALIDATOR = RegexValidator(\n    # regex inspired by above StackOverflow thread\n    regex=r'^([a-zA-Z\\d](?:-?[a-zA-Z\\d])*)$',\n    message='This is not a valid GitHub username.',\n)\n\n\nclass NullableGithubUsernameField(models.CharField):\n    def __init__(self, **kwargs):\n        kwargs.setdefault('null', True)\n        kwargs.setdefault('blank', True)\n        kwargs.setdefault('default', '')\n        # max length of the GH username is 39 characters\n        kwargs.setdefault('max_length', 39)\n        super().__init__(**kwargs)\n\n    default_validators = [\n        GHUSERNAME_MAX_LENGTH_VALIDATOR,\n        GHUSERNAME_REGEX_VALIDATOR,\n    ]\n\n\n#------------------------------------------------------------\n\nclass FakeRequiredMixin:\n    def __init__(self, *args, **kwargs):\n        # Intercept \"fake_required\" attribute that's used for marking field\n        # with \"*\" (asterisk) even though it's not required.\n        # Additionally `fake_required` doesn't trigger any validation.\n        self.fake_required = kwargs.pop('fake_required', False)\n        super().__init__(*args, **kwargs)\n\n\nclass RadioSelectWithOther(FakeRequiredMixin, forms.RadioSelect):\n    \"\"\"A RadioSelect widget that should render additional field ('Other').\n\n    We have a number of occurences of two model fields bound together: one\n    containing predefined set of choices, the other being a text input for\n    other input user wants to choose instead of one of our predefined options.\n\n    This widget should help with rendering two widgets in one table row.\"\"\"\n\n    other_field = None  # to be bound later\n\n    def __init__(self, other_field_name, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.other_field_name = other_field_name\n\n\nclass CheckboxSelectMultipleWithOthers(FakeRequiredMixin, forms.CheckboxSelectMultiple):\n    \"\"\"A multiple choice widget that should render additional field ('Other').\n\n    We have a number of occurences of two model fields bound together: one\n    containing predefined set of choices, the other being a text input for\n    other input user wants to choose instead of one of our predefined options.\n\n    This widget should help with rendering two widgets in one table row.\"\"\"\n\n    other_field = None  # to be bound later\n\n    def __init__(self, other_field_name, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.other_field_name = other_field_name\n\n\nclass RadioSelectFakeMultiple(FakeRequiredMixin, forms.RadioSelect):\n    \"\"\"Pretend to be a radio-select with multiple selection possible. This\n    is intended to 'fool' Django into thinking that user selected 1 item on\n    a multi-select item list.\"\"\"\n    allow_multiple_selected = True\n\n\nclass SafeLabelFromInstanceMixin:\n    def label_from_instance(self, obj):\n        return mark_safe(obj)\n\n\nclass SafeModelChoiceField(SafeLabelFromInstanceMixin, forms.ModelChoiceField):\n    pass\n\n\nclass SafeModelMultipleChoiceField(SafeLabelFromInstanceMixin,\n                                   forms.ModelMultipleChoiceField):\n    pass\n\n\nclass CurriculumModelMultipleChoiceField(SafeModelMultipleChoiceField):\n    def label_from_instance(self, obj):\n        # Display in tooltip (it's a little better than popover, because it\n        # auto-hides and doesn't require clicking on the element, whereas\n        # popover by clicking will automatically select the clicked item)\n        data = (\n            '<a tabindex=\"0\" role=\"button\" data-toggle=\"tooltip\" '\n            'data-placement=\"top\" title=\"{description}\">{obj}</a>'\n            .format(obj=obj, description=obj.description)\n        )\n        return super().label_from_instance(data)\n\n\n#------------------------------------------------------------\n\nclass Select2BootstrapMixin:\n    def build_attrs(self, *args, **kwargs):\n        attrs = super().build_attrs(*args, **kwargs)\n        attrs.setdefault('data-theme', 'bootstrap4')\n        return attrs\n\n\nclass Select2NoMinimumInputLength:\n    def build_attrs(self, *args, **kwargs):\n        # Let's set up the minimum input length first!\n        # It will overwrite `setdefault('data-minimum-input-length')` from\n        # other mixins.\n        self.attrs.setdefault('data-minimum-input-length', 0)\n        attrs = super().build_attrs(*args, **kwargs)\n        return attrs\n\n\n\nclass Select2Widget(FakeRequiredMixin, Select2BootstrapMixin,\n                    DS2_Select2Widget):\n    pass\n\n\nclass Select2MultipleWidget(Select2BootstrapMixin, DS2_Select2MultipleWidget):\n    pass\n\n\nclass ModelSelect2Widget(Select2BootstrapMixin, Select2NoMinimumInputLength,\n                         DS2_ModelSelect2Widget):\n    pass\n\n\nclass ModelSelect2MultipleWidget(Select2BootstrapMixin,\n                                 Select2NoMinimumInputLength,\n                                 DS2_ModelSelect2MultipleWidget):\n    pass\n\n\nTAG_SEPARATOR = ';'\n\n\nclass Select2TagWidget(Select2BootstrapMixin, DS2_Select2TagWidget):\n    def build_attrs(self, base_attrs, extra_attrs=None):\n        \"\"\"Select2's tag attributes. By default other token separators are\n        used, but we want to use \",\" and \";\".\"\"\"\n        default_attrs = {\n            'data-minimum-input-length': 1,\n            'data-tags': 'true',\n            'data-token-separators': '[\",\", \";\"]'\n        }\n        assert TAG_SEPARATOR in default_attrs['data-token-separators']\n\n        default_attrs.update(base_attrs)\n        return super().build_attrs(default_attrs, extra_attrs=extra_attrs)\n\n    def value_from_datadict(self, data, files, name):\n        # sometimes data is held as an immutable QueryDict\n        # in those cases, we need to make a copy of it to \"disable\"\n        # the mutability\n        try:\n            data_mutable = data.copy()\n        except AttributeError:\n            data_mutable = data\n\n        data_mutable.setdefault(name, '')\n        values = super().value_from_datadict(data_mutable, files, name)\n        return TAG_SEPARATOR.join(values)\n\n    def optgroups(self, name, value, attrs=None):\n        \"\"\"Example from\n        https://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget\"\"\"\n        try:\n            values = value[0].split(TAG_SEPARATOR)\n        except (IndexError, AttributeError):\n            values = []\n\n        selected = set(values)\n        subgroup = [\n            self.create_option(name, v, v, selected, i)\n            for i, v in enumerate(values)\n        ]\n        return [(None, subgroup, 0)]\n", "path": "amy/workshops/fields.py"}]}