diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index a2f868c8a3c..caaeef9d0de 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -270,6 +270,13 @@ def clean_parent(self): _('Subproject nesting is not supported')) return self.project + def clean_child(self): + child = self.cleaned_data['child'] + if child == self.project: + raise forms.ValidationError( + _('A project can not be a subproject of itself')) + return child + def get_subproject_queryset(self): """ Return scrubbed subproject choice queryset. @@ -280,7 +287,8 @@ def get_subproject_queryset(self): queryset = ( Project.objects.for_admin_user(self.user) .exclude(subprojects__isnull=False) - .exclude(superprojects__isnull=False)) + .exclude(superprojects__isnull=False) + .exclude(pk=self.project.pk)) return queryset diff --git a/readthedocs/rtd_tests/tests/test_subprojects.py b/readthedocs/rtd_tests/tests/test_subprojects.py index d64d3ee3d6b..1d3ac59aeb3 100644 --- a/readthedocs/rtd_tests/tests/test_subprojects.py +++ b/readthedocs/rtd_tests/tests/test_subprojects.py @@ -143,6 +143,21 @@ def test_excludes_existing_subprojects(self): [''], ) + def test_exclude_self_project_as_subproject(self): + user = fixture.get(User) + project = fixture.get(Project, users=[user]) + + form = ProjectRelationshipForm( + {'child': project.pk}, + project=project, + user=user + ) + self.assertFalse(form.is_valid()) + self.assertNotIn( + project.id, + [proj_id for (proj_id, __) in form.fields['child'].choices] + ) + @override_settings(PUBLIC_DOMAIN='readthedocs.org') class ResolverBase(TestCase):