We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Teams: project form doesn't allow for null/empty project list
I found trying to remove a project from a team that it was impossible to remove the project if it was the last project attached to the team. The form expects some non-null value and throws a validation error if the list is empty.

To reproduce:

- Add a team
- Add a project to the team
- Try to remove the project from the team
- You'll get a validation error on the form

Instead, this should be a valid form submission and the team should have 0 projects attached.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/organizations/forms.py`
Content:
```
1 """Organization forms."""
2 from django import forms
3 from django.contrib.auth.models import User
4 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
5 from django.core.validators import EmailValidator
6 from django.db.models import Q
7 from django.utils.translation import gettext_lazy as _
8 
9 from readthedocs.core.history import SimpleHistoryModelForm
10 from readthedocs.core.permissions import AdminPermission
11 from readthedocs.core.utils import slugify
12 from readthedocs.core.utils.extend import SettingsOverrideObject
13 from readthedocs.invitations.models import Invitation
14 from readthedocs.organizations.constants import ADMIN_ACCESS, READ_ONLY_ACCESS
15 from readthedocs.organizations.models import (
16     Organization,
17     OrganizationOwner,
18     Team,
19     TeamMember,
20 )
21 
22 
23 class OrganizationForm(SimpleHistoryModelForm):
24 
25     """
26     Base organization form.
27 
28     :param user: User instance, responsible for ownership of Organization
29     :type user: django.contrib.auth.models.User
30     """
31 
32     # We use the organization slug + project name
33     # to form the final project slug.
34     # A valid project slug is 63 chars long.
35     name = forms.CharField(max_length=32)
36 
37     class Meta:
38         model = Organization
39         fields = ["name", "email", "description", "url"]
40         labels = {
41             "name": _("Organization Name"),
42             "email": _("Billing Email"),
43         }
44 
45     # Don't use a URLField as a widget, the validation is too strict on FF
46     url = forms.URLField(
47         widget=forms.TextInput(attrs={"placeholder": "http://"}),
48         label=_("Site URL"),
49         required=False,
50     )
51 
52     def __init__(self, *args, **kwargs):
53         try:
54             self.user = kwargs.pop("user")
55         except KeyError:
56             raise TypeError(
57                 "OrganizationForm expects a `user` keyword argument",
58             )
59         super().__init__(*args, **kwargs)
60 
61     def clean_name(self):
62         """Raise exception on duplicate organization slug."""
63         name = self.cleaned_data["name"]
64 
65         # Skip slug validation on already created organizations.
66         if self.instance.pk:
67             return name
68 
69         potential_slug = slugify(name)
70         if not potential_slug:
71             raise forms.ValidationError(
72                 _("Invalid organization name: no slug generated")
73             )
74         if Organization.objects.filter(slug=potential_slug).exists():
75             raise forms.ValidationError(
76                 _("Organization %(name)s already exists"),
77                 params={"name": name},
78             )
79         return name
80 
81 
82 class OrganizationSignupFormBase(OrganizationForm):
83 
84     """
85     Simple organization creation form.
86 
87     This trims down the number of inputs required to create a new organization.
88     This is used on the initial organization signup, to keep signup terse.
89 
90     :param user: User instance, responsible for ownership of Organization
91     :type user: django.contrib.auth.models.User
92     """
93 
94     class Meta:
95         model = Organization
96         fields = ["name", "email"]
97         labels = {
98             "name": _("Organization Name"),
99             "email": _("Billing Email"),
100         }
101 
102     url = None
103 
104     @staticmethod
105     def _create_default_teams(organization):
106         organization.teams.create(name="Admins", access=ADMIN_ACCESS)
107         organization.teams.create(name="Read Only", access=READ_ONLY_ACCESS)
108 
109     def save(self, commit=True):
110         org = super().save(commit)
111 
112         # If not committing, we can't save M2M fields
113         if not commit:
114             return org
115 
116         # Add default teams
117         OrganizationOwner.objects.create(
118             owner=self.user,
119             organization=org,
120         )
121         self._create_default_teams(org)
122         return org
123 
124 
125 class OrganizationSignupForm(SettingsOverrideObject):
126     _default_class = OrganizationSignupFormBase
127 
128 
129 class OrganizationOwnerForm(forms.Form):
130 
131     """Form to manage owners of the organization."""
132 
133     username_or_email = forms.CharField(label=_("Email address or username"))
134 
135     def __init__(self, *args, **kwargs):
136         self.organization = kwargs.pop("organization", None)
137         self.request = kwargs.pop("request", None)
138         super().__init__(*args, **kwargs)
139 
140     def clean_username_or_email(self):
141         """Lookup owner by username or email, detect collisions with existing owners."""
142         username = self.cleaned_data["username_or_email"]
143         user = User.objects.filter(
144             Q(username=username)
145             | Q(emailaddress__verified=True, emailaddress__email=username)
146         ).first()
147         if user is None:
148             raise forms.ValidationError(
149                 _("User %(username)s does not exist"),
150                 params={"username": username},
151             )
152         if self.organization.owners.filter(pk=user.pk).exists():
153             raise forms.ValidationError(
154                 _("User %(username)s is already an owner"),
155                 params={"username": username},
156             )
157         return user
158 
159     def save(self):
160         invitation, _ = Invitation.objects.invite(
161             from_user=self.request.user,
162             to_user=self.cleaned_data["username_or_email"],
163             obj=self.organization,
164             request=self.request,
165         )
166         return invitation
167 
168 
169 class OrganizationTeamBasicFormBase(SimpleHistoryModelForm):
170 
171     """Form to manage teams."""
172 
173     class Meta:
174         model = Team
175         fields = ["name", "access", "organization"]
176         error_messages = {
177             NON_FIELD_ERRORS: {
178                 "unique_together": _("Team already exists"),
179             },
180         }
181 
182     organization = forms.CharField(widget=forms.HiddenInput(), required=False)
183 
184     def __init__(self, *args, **kwargs):
185         self.organization = kwargs.pop("organization", None)
186         super().__init__(*args, **kwargs)
187 
188     def clean_organization(self):
189         """Hard code organization return on form."""
190         return self.organization
191 
192 
193 class OrganizationTeamBasicForm(SettingsOverrideObject):
194     _default_class = OrganizationTeamBasicFormBase
195 
196 
197 class OrganizationTeamProjectForm(forms.ModelForm):
198 
199     """Form to manage access of teams to projects."""
200 
201     class Meta:
202         model = Team
203         fields = ["projects"]
204 
205     def __init__(self, *args, **kwargs):
206         self.organization = kwargs.pop("organization", None)
207         super().__init__(*args, **kwargs)
208         self.fields["projects"] = forms.ModelMultipleChoiceField(
209             queryset=self.organization.projects,
210             widget=forms.CheckboxSelectMultiple,
211         )
212 
213 
214 class OrganizationTeamMemberForm(forms.Form):
215 
216     """Form to manage all members of the organization."""
217 
218     username_or_email = forms.CharField(label=_("Email address or username"))
219 
220     def __init__(self, *args, **kwargs):
221         self.team = kwargs.pop("team", None)
222         self.request = kwargs.pop("request", None)
223         super().__init__(*args, **kwargs)
224 
225     def clean_username_or_email(self):
226         """
227         Validate the user to invite to.
228 
229         We search for an existing user by username or email,
230         if none is found, we try to validate if the input is an email,
231         in that case we send an invitation to that email.
232         """
233         username = self.cleaned_data["username_or_email"]
234         user = User.objects.filter(
235             Q(username=username)
236             | Q(emailaddress__verified=True, emailaddress__email=username)
237         ).first()
238 
239         if user:
240             return self.validate_member_user(user)
241 
242         # If it's a valid email,
243         # then try sending an invitation to it.
244         try:
245             validator = EmailValidator(code="lookup not an email")
246             validator(username)
247             return username
248         except ValidationError as error:
249             if error.code != "lookup not an email":
250                 raise
251 
252         raise forms.ValidationError(
253             _("User %(username)s does not exist"), params={"username": username}
254         )
255 
256     def validate_member_user(self, member):
257         """Verify duplicate team member doesn't already exists."""
258         if TeamMember.objects.filter(team=self.team, member=member).exists():
259             raise forms.ValidationError(
260                 _("User is already a team member"),
261             )
262         return member
263 
264     def save(self):
265         """Create an invitation only if the user isn't already a member."""
266         user = self.cleaned_data["username_or_email"]
267         if isinstance(user, User):
268             # If the user is already a member or the organization
269             # don't create an invitation.
270             if (
271                 AdminPermission.members(self.team.organization)
272                 .filter(pk=user.pk)
273                 .exists()
274             ):
275                 member = self.team.organization.add_member(user, self.team)
276                 if user != self.request.user:
277                     member.send_add_notification(self.request)
278                 return user
279             invitation, _ = Invitation.objects.invite(
280                 from_user=self.request.user,
281                 to_user=user,
282                 obj=self.team,
283                 request=self.request,
284             )
285             return invitation
286         invitation, _ = Invitation.objects.invite(
287             from_user=self.request.user,
288             to_email=user,
289             obj=self.team,
290             request=self.request,
291         )
292         return invitation
293
```
--- 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/readthedocs/organizations/forms.py b/readthedocs/organizations/forms.py
--- a/readthedocs/organizations/forms.py
+++ b/readthedocs/organizations/forms.py
@@ -208,6 +208,7 @@
         self.fields["projects"] = forms.ModelMultipleChoiceField(
             queryset=self.organization.projects,
             widget=forms.CheckboxSelectMultiple,
+            required=False,
         )

{"golden_diff": "diff --git a/readthedocs/organizations/forms.py b/readthedocs/organizations/forms.py\n--- a/readthedocs/organizations/forms.py\n+++ b/readthedocs/organizations/forms.py\n@@ -208,6 +208,7 @@\n         self.fields[\"projects\"] = forms.ModelMultipleChoiceField(\n             queryset=self.organization.projects,\n             widget=forms.CheckboxSelectMultiple,\n+            required=False,\n         )\n", "issue": "Teams: project form doesn't allow for null/empty project list\nI found trying to remove a project from a team that it was impossible to remove the project if it was the last project attached to the team. The form expects some non-null value and throws a validation error if the list is empty.\r\n\r\nTo reproduce:\r\n\r\n- Add a team\r\n- Add a project to the team\r\n- Try to remove the project from the team\r\n- You'll get a validation error on the form\r\n\r\nInstead, this should be a valid form submission and the team should have 0 projects attached.\n", "before_files": [{"content": "\"\"\"Organization forms.\"\"\"\nfrom django import forms\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import NON_FIELD_ERRORS, ValidationError\nfrom django.core.validators import EmailValidator\nfrom django.db.models import Q\nfrom django.utils.translation import gettext_lazy as _\n\nfrom readthedocs.core.history import SimpleHistoryModelForm\nfrom readthedocs.core.permissions import AdminPermission\nfrom readthedocs.core.utils import slugify\nfrom readthedocs.core.utils.extend import SettingsOverrideObject\nfrom readthedocs.invitations.models import Invitation\nfrom readthedocs.organizations.constants import ADMIN_ACCESS, READ_ONLY_ACCESS\nfrom readthedocs.organizations.models import (\n    Organization,\n    OrganizationOwner,\n    Team,\n    TeamMember,\n)\n\n\nclass OrganizationForm(SimpleHistoryModelForm):\n\n    \"\"\"\n    Base organization form.\n\n    :param user: User instance, responsible for ownership of Organization\n    :type user: django.contrib.auth.models.User\n    \"\"\"\n\n    # We use the organization slug + project name\n    # to form the final project slug.\n    # A valid project slug is 63 chars long.\n    name = forms.CharField(max_length=32)\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"email\", \"description\", \"url\"]\n        labels = {\n            \"name\": _(\"Organization Name\"),\n            \"email\": _(\"Billing Email\"),\n        }\n\n    # Don't use a URLField as a widget, the validation is too strict on FF\n    url = forms.URLField(\n        widget=forms.TextInput(attrs={\"placeholder\": \"http://\"}),\n        label=_(\"Site URL\"),\n        required=False,\n    )\n\n    def __init__(self, *args, **kwargs):\n        try:\n            self.user = kwargs.pop(\"user\")\n        except KeyError:\n            raise TypeError(\n                \"OrganizationForm expects a `user` keyword argument\",\n            )\n        super().__init__(*args, **kwargs)\n\n    def clean_name(self):\n        \"\"\"Raise exception on duplicate organization slug.\"\"\"\n        name = self.cleaned_data[\"name\"]\n\n        # Skip slug validation on already created organizations.\n        if self.instance.pk:\n            return name\n\n        potential_slug = slugify(name)\n        if not potential_slug:\n            raise forms.ValidationError(\n                _(\"Invalid organization name: no slug generated\")\n            )\n        if Organization.objects.filter(slug=potential_slug).exists():\n            raise forms.ValidationError(\n                _(\"Organization %(name)s already exists\"),\n                params={\"name\": name},\n            )\n        return name\n\n\nclass OrganizationSignupFormBase(OrganizationForm):\n\n    \"\"\"\n    Simple organization creation form.\n\n    This trims down the number of inputs required to create a new organization.\n    This is used on the initial organization signup, to keep signup terse.\n\n    :param user: User instance, responsible for ownership of Organization\n    :type user: django.contrib.auth.models.User\n    \"\"\"\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"email\"]\n        labels = {\n            \"name\": _(\"Organization Name\"),\n            \"email\": _(\"Billing Email\"),\n        }\n\n    url = None\n\n    @staticmethod\n    def _create_default_teams(organization):\n        organization.teams.create(name=\"Admins\", access=ADMIN_ACCESS)\n        organization.teams.create(name=\"Read Only\", access=READ_ONLY_ACCESS)\n\n    def save(self, commit=True):\n        org = super().save(commit)\n\n        # If not committing, we can't save M2M fields\n        if not commit:\n            return org\n\n        # Add default teams\n        OrganizationOwner.objects.create(\n            owner=self.user,\n            organization=org,\n        )\n        self._create_default_teams(org)\n        return org\n\n\nclass OrganizationSignupForm(SettingsOverrideObject):\n    _default_class = OrganizationSignupFormBase\n\n\nclass OrganizationOwnerForm(forms.Form):\n\n    \"\"\"Form to manage owners of the organization.\"\"\"\n\n    username_or_email = forms.CharField(label=_(\"Email address or username\"))\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        self.request = kwargs.pop(\"request\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_username_or_email(self):\n        \"\"\"Lookup owner by username or email, detect collisions with existing owners.\"\"\"\n        username = self.cleaned_data[\"username_or_email\"]\n        user = User.objects.filter(\n            Q(username=username)\n            | Q(emailaddress__verified=True, emailaddress__email=username)\n        ).first()\n        if user is None:\n            raise forms.ValidationError(\n                _(\"User %(username)s does not exist\"),\n                params={\"username\": username},\n            )\n        if self.organization.owners.filter(pk=user.pk).exists():\n            raise forms.ValidationError(\n                _(\"User %(username)s is already an owner\"),\n                params={\"username\": username},\n            )\n        return user\n\n    def save(self):\n        invitation, _ = Invitation.objects.invite(\n            from_user=self.request.user,\n            to_user=self.cleaned_data[\"username_or_email\"],\n            obj=self.organization,\n            request=self.request,\n        )\n        return invitation\n\n\nclass OrganizationTeamBasicFormBase(SimpleHistoryModelForm):\n\n    \"\"\"Form to manage teams.\"\"\"\n\n    class Meta:\n        model = Team\n        fields = [\"name\", \"access\", \"organization\"]\n        error_messages = {\n            NON_FIELD_ERRORS: {\n                \"unique_together\": _(\"Team already exists\"),\n            },\n        }\n\n    organization = forms.CharField(widget=forms.HiddenInput(), required=False)\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_organization(self):\n        \"\"\"Hard code organization return on form.\"\"\"\n        return self.organization\n\n\nclass OrganizationTeamBasicForm(SettingsOverrideObject):\n    _default_class = OrganizationTeamBasicFormBase\n\n\nclass OrganizationTeamProjectForm(forms.ModelForm):\n\n    \"\"\"Form to manage access of teams to projects.\"\"\"\n\n    class Meta:\n        model = Team\n        fields = [\"projects\"]\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        super().__init__(*args, **kwargs)\n        self.fields[\"projects\"] = forms.ModelMultipleChoiceField(\n            queryset=self.organization.projects,\n            widget=forms.CheckboxSelectMultiple,\n        )\n\n\nclass OrganizationTeamMemberForm(forms.Form):\n\n    \"\"\"Form to manage all members of the organization.\"\"\"\n\n    username_or_email = forms.CharField(label=_(\"Email address or username\"))\n\n    def __init__(self, *args, **kwargs):\n        self.team = kwargs.pop(\"team\", None)\n        self.request = kwargs.pop(\"request\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_username_or_email(self):\n        \"\"\"\n        Validate the user to invite to.\n\n        We search for an existing user by username or email,\n        if none is found, we try to validate if the input is an email,\n        in that case we send an invitation to that email.\n        \"\"\"\n        username = self.cleaned_data[\"username_or_email\"]\n        user = User.objects.filter(\n            Q(username=username)\n            | Q(emailaddress__verified=True, emailaddress__email=username)\n        ).first()\n\n        if user:\n            return self.validate_member_user(user)\n\n        # If it's a valid email,\n        # then try sending an invitation to it.\n        try:\n            validator = EmailValidator(code=\"lookup not an email\")\n            validator(username)\n            return username\n        except ValidationError as error:\n            if error.code != \"lookup not an email\":\n                raise\n\n        raise forms.ValidationError(\n            _(\"User %(username)s does not exist\"), params={\"username\": username}\n        )\n\n    def validate_member_user(self, member):\n        \"\"\"Verify duplicate team member doesn't already exists.\"\"\"\n        if TeamMember.objects.filter(team=self.team, member=member).exists():\n            raise forms.ValidationError(\n                _(\"User is already a team member\"),\n            )\n        return member\n\n    def save(self):\n        \"\"\"Create an invitation only if the user isn't already a member.\"\"\"\n        user = self.cleaned_data[\"username_or_email\"]\n        if isinstance(user, User):\n            # If the user is already a member or the organization\n            # don't create an invitation.\n            if (\n                AdminPermission.members(self.team.organization)\n                .filter(pk=user.pk)\n                .exists()\n            ):\n                member = self.team.organization.add_member(user, self.team)\n                if user != self.request.user:\n                    member.send_add_notification(self.request)\n                return user\n            invitation, _ = Invitation.objects.invite(\n                from_user=self.request.user,\n                to_user=user,\n                obj=self.team,\n                request=self.request,\n            )\n            return invitation\n        invitation, _ = Invitation.objects.invite(\n            from_user=self.request.user,\n            to_email=user,\n            obj=self.team,\n            request=self.request,\n        )\n        return invitation\n", "path": "readthedocs/organizations/forms.py"}], "after_files": [{"content": "\"\"\"Organization forms.\"\"\"\nfrom django import forms\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import NON_FIELD_ERRORS, ValidationError\nfrom django.core.validators import EmailValidator\nfrom django.db.models import Q\nfrom django.utils.translation import gettext_lazy as _\n\nfrom readthedocs.core.history import SimpleHistoryModelForm\nfrom readthedocs.core.permissions import AdminPermission\nfrom readthedocs.core.utils import slugify\nfrom readthedocs.core.utils.extend import SettingsOverrideObject\nfrom readthedocs.invitations.models import Invitation\nfrom readthedocs.organizations.constants import ADMIN_ACCESS, READ_ONLY_ACCESS\nfrom readthedocs.organizations.models import (\n    Organization,\n    OrganizationOwner,\n    Team,\n    TeamMember,\n)\n\n\nclass OrganizationForm(SimpleHistoryModelForm):\n\n    \"\"\"\n    Base organization form.\n\n    :param user: User instance, responsible for ownership of Organization\n    :type user: django.contrib.auth.models.User\n    \"\"\"\n\n    # We use the organization slug + project name\n    # to form the final project slug.\n    # A valid project slug is 63 chars long.\n    name = forms.CharField(max_length=32)\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"email\", \"description\", \"url\"]\n        labels = {\n            \"name\": _(\"Organization Name\"),\n            \"email\": _(\"Billing Email\"),\n        }\n\n    # Don't use a URLField as a widget, the validation is too strict on FF\n    url = forms.URLField(\n        widget=forms.TextInput(attrs={\"placeholder\": \"http://\"}),\n        label=_(\"Site URL\"),\n        required=False,\n    )\n\n    def __init__(self, *args, **kwargs):\n        try:\n            self.user = kwargs.pop(\"user\")\n        except KeyError:\n            raise TypeError(\n                \"OrganizationForm expects a `user` keyword argument\",\n            )\n        super().__init__(*args, **kwargs)\n\n    def clean_name(self):\n        \"\"\"Raise exception on duplicate organization slug.\"\"\"\n        name = self.cleaned_data[\"name\"]\n\n        # Skip slug validation on already created organizations.\n        if self.instance.pk:\n            return name\n\n        potential_slug = slugify(name)\n        if not potential_slug:\n            raise forms.ValidationError(\n                _(\"Invalid organization name: no slug generated\")\n            )\n        if Organization.objects.filter(slug=potential_slug).exists():\n            raise forms.ValidationError(\n                _(\"Organization %(name)s already exists\"),\n                params={\"name\": name},\n            )\n        return name\n\n\nclass OrganizationSignupFormBase(OrganizationForm):\n\n    \"\"\"\n    Simple organization creation form.\n\n    This trims down the number of inputs required to create a new organization.\n    This is used on the initial organization signup, to keep signup terse.\n\n    :param user: User instance, responsible for ownership of Organization\n    :type user: django.contrib.auth.models.User\n    \"\"\"\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"email\"]\n        labels = {\n            \"name\": _(\"Organization Name\"),\n            \"email\": _(\"Billing Email\"),\n        }\n\n    url = None\n\n    @staticmethod\n    def _create_default_teams(organization):\n        organization.teams.create(name=\"Admins\", access=ADMIN_ACCESS)\n        organization.teams.create(name=\"Read Only\", access=READ_ONLY_ACCESS)\n\n    def save(self, commit=True):\n        org = super().save(commit)\n\n        # If not committing, we can't save M2M fields\n        if not commit:\n            return org\n\n        # Add default teams\n        OrganizationOwner.objects.create(\n            owner=self.user,\n            organization=org,\n        )\n        self._create_default_teams(org)\n        return org\n\n\nclass OrganizationSignupForm(SettingsOverrideObject):\n    _default_class = OrganizationSignupFormBase\n\n\nclass OrganizationOwnerForm(forms.Form):\n\n    \"\"\"Form to manage owners of the organization.\"\"\"\n\n    username_or_email = forms.CharField(label=_(\"Email address or username\"))\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        self.request = kwargs.pop(\"request\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_username_or_email(self):\n        \"\"\"Lookup owner by username or email, detect collisions with existing owners.\"\"\"\n        username = self.cleaned_data[\"username_or_email\"]\n        user = User.objects.filter(\n            Q(username=username)\n            | Q(emailaddress__verified=True, emailaddress__email=username)\n        ).first()\n        if user is None:\n            raise forms.ValidationError(\n                _(\"User %(username)s does not exist\"),\n                params={\"username\": username},\n            )\n        if self.organization.owners.filter(pk=user.pk).exists():\n            raise forms.ValidationError(\n                _(\"User %(username)s is already an owner\"),\n                params={\"username\": username},\n            )\n        return user\n\n    def save(self):\n        invitation, _ = Invitation.objects.invite(\n            from_user=self.request.user,\n            to_user=self.cleaned_data[\"username_or_email\"],\n            obj=self.organization,\n            request=self.request,\n        )\n        return invitation\n\n\nclass OrganizationTeamBasicFormBase(SimpleHistoryModelForm):\n\n    \"\"\"Form to manage teams.\"\"\"\n\n    class Meta:\n        model = Team\n        fields = [\"name\", \"access\", \"organization\"]\n        error_messages = {\n            NON_FIELD_ERRORS: {\n                \"unique_together\": _(\"Team already exists\"),\n            },\n        }\n\n    organization = forms.CharField(widget=forms.HiddenInput(), required=False)\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_organization(self):\n        \"\"\"Hard code organization return on form.\"\"\"\n        return self.organization\n\n\nclass OrganizationTeamBasicForm(SettingsOverrideObject):\n    _default_class = OrganizationTeamBasicFormBase\n\n\nclass OrganizationTeamProjectForm(forms.ModelForm):\n\n    \"\"\"Form to manage access of teams to projects.\"\"\"\n\n    class Meta:\n        model = Team\n        fields = [\"projects\"]\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\", None)\n        super().__init__(*args, **kwargs)\n        self.fields[\"projects\"] = forms.ModelMultipleChoiceField(\n            queryset=self.organization.projects,\n            widget=forms.CheckboxSelectMultiple,\n            required=False,\n        )\n\n\nclass OrganizationTeamMemberForm(forms.Form):\n\n    \"\"\"Form to manage all members of the organization.\"\"\"\n\n    username_or_email = forms.CharField(label=_(\"Email address or username\"))\n\n    def __init__(self, *args, **kwargs):\n        self.team = kwargs.pop(\"team\", None)\n        self.request = kwargs.pop(\"request\", None)\n        super().__init__(*args, **kwargs)\n\n    def clean_username_or_email(self):\n        \"\"\"\n        Validate the user to invite to.\n\n        We search for an existing user by username or email,\n        if none is found, we try to validate if the input is an email,\n        in that case we send an invitation to that email.\n        \"\"\"\n        username = self.cleaned_data[\"username_or_email\"]\n        user = User.objects.filter(\n            Q(username=username)\n            | Q(emailaddress__verified=True, emailaddress__email=username)\n        ).first()\n\n        if user:\n            return self.validate_member_user(user)\n\n        # If it's a valid email,\n        # then try sending an invitation to it.\n        try:\n            validator = EmailValidator(code=\"lookup not an email\")\n            validator(username)\n            return username\n        except ValidationError as error:\n            if error.code != \"lookup not an email\":\n                raise\n\n        raise forms.ValidationError(\n            _(\"User %(username)s does not exist\"), params={\"username\": username}\n        )\n\n    def validate_member_user(self, member):\n        \"\"\"Verify duplicate team member doesn't already exists.\"\"\"\n        if TeamMember.objects.filter(team=self.team, member=member).exists():\n            raise forms.ValidationError(\n                _(\"User is already a team member\"),\n            )\n        return member\n\n    def save(self):\n        \"\"\"Create an invitation only if the user isn't already a member.\"\"\"\n        user = self.cleaned_data[\"username_or_email\"]\n        if isinstance(user, User):\n            # If the user is already a member or the organization\n            # don't create an invitation.\n            if (\n                AdminPermission.members(self.team.organization)\n                .filter(pk=user.pk)\n                .exists()\n            ):\n                member = self.team.organization.add_member(user, self.team)\n                if user != self.request.user:\n                    member.send_add_notification(self.request)\n                return user\n            invitation, _ = Invitation.objects.invite(\n                from_user=self.request.user,\n                to_user=user,\n                obj=self.team,\n                request=self.request,\n            )\n            return invitation\n        invitation, _ = Invitation.objects.invite(\n            from_user=self.request.user,\n            to_email=user,\n            obj=self.team,\n            request=self.request,\n        )\n        return invitation\n", "path": "readthedocs/organizations/forms.py"}]}