|
18 | 18 | PRIVACY_CHOICES,
|
19 | 19 | PROTECTED,
|
20 | 20 | )
|
21 |
| -from readthedocs.projects.models import Project, EnvironmentVariable |
| 21 | +from readthedocs.projects.models import Project, EnvironmentVariable, ProjectRelationship |
22 | 22 | from readthedocs.redirects.models import Redirect, TYPE_CHOICES as REDIRECT_TYPE_CHOICES
|
23 | 23 |
|
24 | 24 |
|
@@ -385,7 +385,7 @@ def get_subprojects(self, obj):
|
385 | 385 | path = reverse(
|
386 | 386 | 'projects-subprojects-list',
|
387 | 387 | kwargs={
|
388 |
| - 'parent_lookup_superprojects__parent__slug': obj.slug, |
| 388 | + 'parent_lookup_parent__slug': obj.slug, |
389 | 389 | },
|
390 | 390 | )
|
391 | 391 | return self._absolute_url(path)
|
@@ -539,6 +539,128 @@ def get_subproject_of(self, obj):
|
539 | 539 | return None
|
540 | 540 |
|
541 | 541 |
|
| 542 | +class SubprojectCreateSerializer(FlexFieldsModelSerializer): |
| 543 | + |
| 544 | + """Serializer used to define a Project as subproject of another Project.""" |
| 545 | + |
| 546 | + child = serializers.SlugRelatedField( |
| 547 | + slug_field='slug', |
| 548 | + queryset=Project.objects.all(), |
| 549 | + ) |
| 550 | + |
| 551 | + class Meta: |
| 552 | + model = ProjectRelationship |
| 553 | + fields = [ |
| 554 | + 'child', |
| 555 | + 'alias', |
| 556 | + ] |
| 557 | + |
| 558 | + def __init__(self, *args, **kwargs): |
| 559 | + # Initialize the instance with the parent Project to be used in the |
| 560 | + # serializer validation. |
| 561 | + self.parent_project = kwargs.pop('parent') |
| 562 | + super().__init__(*args, **kwargs) |
| 563 | + |
| 564 | + def validate_child(self, value): |
| 565 | + # Check the user is maintainer of the child project |
| 566 | + user = self.context['request'].user |
| 567 | + if user not in value.users.all(): |
| 568 | + raise serializers.ValidationError( |
| 569 | + 'You do not have permissions on the child project', |
| 570 | + ) |
| 571 | + return value |
| 572 | + |
| 573 | + def validate_alias(self, value): |
| 574 | + # Check there is not a subproject with this alias already |
| 575 | + subproject = self.parent_project.subprojects.filter(alias=value) |
| 576 | + if subproject.exists(): |
| 577 | + raise serializers.ValidationError( |
| 578 | + 'A subproject with this alias already exists', |
| 579 | + ) |
| 580 | + return value |
| 581 | + |
| 582 | + # pylint: disable=arguments-differ |
| 583 | + def validate(self, data): |
| 584 | + # Check the parent and child are not the same project |
| 585 | + if data['child'].slug == self.parent_project.slug: |
| 586 | + raise serializers.ValidationError( |
| 587 | + 'Project can not be subproject of itself', |
| 588 | + ) |
| 589 | + |
| 590 | + # Check the parent project is not a subproject already |
| 591 | + if self.parent_project.superprojects.exists(): |
| 592 | + raise serializers.ValidationError( |
| 593 | + 'Subproject nesting is not supported', |
| 594 | + ) |
| 595 | + return data |
| 596 | + |
| 597 | + |
| 598 | +class SubprojectLinksSerializer(BaseLinksSerializer): |
| 599 | + _self = serializers.SerializerMethodField() |
| 600 | + parent = serializers.SerializerMethodField() |
| 601 | + |
| 602 | + def get__self(self, obj): |
| 603 | + path = reverse( |
| 604 | + 'projects-subprojects-detail', |
| 605 | + kwargs={ |
| 606 | + 'parent_lookup_parent__slug': obj.parent.slug, |
| 607 | + 'alias_slug': obj.alias, |
| 608 | + }, |
| 609 | + ) |
| 610 | + return self._absolute_url(path) |
| 611 | + |
| 612 | + def get_parent(self, obj): |
| 613 | + path = reverse( |
| 614 | + 'projects-detail', |
| 615 | + kwargs={ |
| 616 | + 'project_slug': obj.parent.slug, |
| 617 | + }, |
| 618 | + ) |
| 619 | + return self._absolute_url(path) |
| 620 | + |
| 621 | + |
| 622 | +class ChildProjectSerializer(ProjectSerializer): |
| 623 | + |
| 624 | + """ |
| 625 | + Serializer to render a Project when listed under ProjectRelationship. |
| 626 | +
|
| 627 | + It's exactly the same as ``ProjectSerializer`` but without some fields. |
| 628 | + """ |
| 629 | + |
| 630 | + class Meta(ProjectSerializer.Meta): |
| 631 | + fields = [ |
| 632 | + field for field in ProjectSerializer.Meta.fields |
| 633 | + if field not in ['subproject_of'] |
| 634 | + ] |
| 635 | + |
| 636 | + |
| 637 | +class SubprojectSerializer(FlexFieldsModelSerializer): |
| 638 | + |
| 639 | + """Serializer to render a subproject (``ProjectRelationship``).""" |
| 640 | + |
| 641 | + child = ChildProjectSerializer() |
| 642 | + _links = SubprojectLinksSerializer(source='*') |
| 643 | + |
| 644 | + class Meta: |
| 645 | + model = ProjectRelationship |
| 646 | + fields = [ |
| 647 | + 'child', |
| 648 | + 'alias', |
| 649 | + '_links', |
| 650 | + ] |
| 651 | + |
| 652 | + |
| 653 | +class SubprojectDestroySerializer(FlexFieldsModelSerializer): |
| 654 | + |
| 655 | + """Serializer used to remove a subproject relationship to a Project.""" |
| 656 | + |
| 657 | + class Meta: |
| 658 | + model = ProjectRelationship |
| 659 | + fields = ( |
| 660 | + 'alias', |
| 661 | + ) |
| 662 | + |
| 663 | + |
542 | 664 | class RedirectLinksSerializer(BaseLinksSerializer):
|
543 | 665 | _self = serializers.SerializerMethodField()
|
544 | 666 | project = serializers.SerializerMethodField()
|
|
0 commit comments