Skip to content

Refactor Subproject validation to use it for Forms and API #6285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 6 additions & 22 deletions readthedocs/api/v3/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,17 +572,9 @@ def validate_child(self, value):
_('You do not have permissions on the child project'),
)

# Check the child project is not a subproject already
if value.superprojects.exists():
raise serializers.ValidationError(
_('Child is already a subproject of another project'),
)

# Check the child project is already a superproject
if value.subprojects.exists():
raise serializers.ValidationError(
_('Child is already a superproject'),
)
value.is_valid_as_subproject(
self.parent_project, serializers.ValidationError
)
return value

def validate_alias(self, value):
Expand All @@ -596,17 +588,9 @@ def validate_alias(self, value):

# pylint: disable=arguments-differ
def validate(self, data):
# Check the parent and child are not the same project
if data['child'].slug == self.parent_project.slug:
raise serializers.ValidationError(
_('Project can not be subproject of itself'),
)

# Check the parent project is not a subproject already
if self.parent_project.superprojects.exists():
raise serializers.ValidationError(
_('Subproject nesting is not supported'),
)
self.parent_project.is_valid_as_superproject(
serializers.ValidationError
)
return data


Expand Down
2 changes: 1 addition & 1 deletion readthedocs/api/v3/tests/test_subprojects.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_projects_subprojects_list_post_with_subproject_of_itself(self):
self.assertEqual(response.status_code, 400)
self.assertIn(
'Project can not be subproject of itself',
response.json()['non_field_errors'],
response.json()['child'],
)
self.assertEqual(newproject.subprojects.count(), 0)

Expand Down
17 changes: 7 additions & 10 deletions readthedocs/projects/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,20 +360,17 @@ def __init__(self, *args, **kwargs):
self.fields['child'].queryset = self.get_subproject_queryset()

def clean_parent(self):
if self.project.superprojects.exists():
# This validation error is mostly for testing, users shouldn't see
# this in normal circumstances
raise forms.ValidationError(
_('Subproject nesting is not supported'),
)
self.project.is_valid_as_superproject(
forms.ValidationError
)
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'),
)

child.is_valid_as_subproject(
self.project, forms.ValidationError
)
return child

def clean_alias(self):
Expand Down
38 changes: 38 additions & 0 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,44 @@ def environment_variables(self):
for variable in self.environmentvariable_set.all()
}

def is_valid_as_superproject(self, error_class):
"""
Checks if the project can be a superproject.

This is used to handle form and serializer validations
if check fails returns ValidationError using to the error_class passed
"""
# Check the parent project is not a subproject already
if self.superprojects.exists():
raise error_class(
_('Subproject nesting is not supported'),
)

def is_valid_as_subproject(self, parent, error_class):
"""
Checks if the project can be a subproject.

This is used to handle form and serializer validations
if check fails returns ValidationError using to the error_class passed
"""
# Check the child project is not a subproject already
if self.superprojects.exists():
raise error_class(
_('Child is already a subproject of another project'),
)

# Check the child project is already a superproject
if self.subprojects.exists():
raise error_class(
_('Child is already a superproject'),
)

# Check the parent and child are not the same project
if parent.slug == self.slug:
raise error_class(
_('Project can not be subproject of itself'),
)


class APIProject(Project):

Expand Down