diff --git a/taiga/projects/custom_attributes/api.py b/taiga/projects/custom_attributes/api.py
index 9bfc774f..2d05d186 100644
--- a/taiga/projects/custom_attributes/api.py
+++ b/taiga/projects/custom_attributes/api.py
@@ -32,6 +32,7 @@ from taiga.projects.occ.mixins import OCCResourceMixin
from . import models
from . import serializers
+from . import validators
from . import permissions
from . import services
@@ -43,6 +44,7 @@ from . import services
class UserStoryCustomAttributeViewSet(BulkUpdateOrderMixin, BlockedByProjectMixin, ModelCrudViewSet):
model = models.UserStoryCustomAttribute
serializer_class = serializers.UserStoryCustomAttributeSerializer
+ validator_class = validators.UserStoryCustomAttributeValidator
permission_classes = (permissions.UserStoryCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
@@ -54,6 +56,7 @@ class UserStoryCustomAttributeViewSet(BulkUpdateOrderMixin, BlockedByProjectMixi
class TaskCustomAttributeViewSet(BulkUpdateOrderMixin, BlockedByProjectMixin, ModelCrudViewSet):
model = models.TaskCustomAttribute
serializer_class = serializers.TaskCustomAttributeSerializer
+ validator_class = validators.TaskCustomAttributeValidator
permission_classes = (permissions.TaskCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
@@ -65,6 +68,7 @@ class TaskCustomAttributeViewSet(BulkUpdateOrderMixin, BlockedByProjectMixin, Mo
class IssueCustomAttributeViewSet(BulkUpdateOrderMixin, BlockedByProjectMixin, ModelCrudViewSet):
model = models.IssueCustomAttribute
serializer_class = serializers.IssueCustomAttributeSerializer
+ validator_class = validators.IssueCustomAttributeValidator
permission_classes = (permissions.IssueCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
@@ -86,6 +90,7 @@ class BaseCustomAttributesValuesViewSet(OCCResourceMixin, HistoryResourceMixin,
class UserStoryCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.UserStoryCustomAttributesValues
serializer_class = serializers.UserStoryCustomAttributesValuesSerializer
+ validator_class = validators.UserStoryCustomAttributesValuesValidator
permission_classes = (permissions.UserStoryCustomAttributesValuesPermission,)
lookup_field = "user_story_id"
content_object = "user_story"
@@ -99,6 +104,7 @@ class UserStoryCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
class TaskCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.TaskCustomAttributesValues
serializer_class = serializers.TaskCustomAttributesValuesSerializer
+ validator_class = validators.TaskCustomAttributesValuesValidator
permission_classes = (permissions.TaskCustomAttributesValuesPermission,)
lookup_field = "task_id"
content_object = "task"
@@ -112,6 +118,7 @@ class TaskCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
class IssueCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.IssueCustomAttributesValues
serializer_class = serializers.IssueCustomAttributesValuesSerializer
+ validator_class = validators.IssueCustomAttributesValuesValidator
permission_classes = (permissions.IssueCustomAttributesValuesPermission,)
lookup_field = "issue_id"
content_object = "issue"
diff --git a/taiga/projects/custom_attributes/serializers.py b/taiga/projects/custom_attributes/serializers.py
index 64a934f5..d4fc084e 100644
--- a/taiga/projects/custom_attributes/serializers.py
+++ b/taiga/projects/custom_attributes/serializers.py
@@ -17,131 +17,51 @@
# along with this program. If not, see .
-from django.apps import apps
-from django.utils.translation import ugettext_lazy as _
-
-from taiga.base.fields import JsonField
-from taiga.base.api.serializers import ValidationError
-from taiga.base.api.serializers import ModelSerializer
-
-from . import models
+from taiga.base.fields import JsonField, Field
+from taiga.base.api import serializers
######################################################
# Custom Attribute Serializer
#######################################################
-class BaseCustomAttributeSerializer(ModelSerializer):
- class Meta:
- read_only_fields = ('id',)
- exclude = ('created_date', 'modified_date')
-
- def _validate_integrity_between_project_and_name(self, attrs, source):
- """
- Check the name is not duplicated in the project. Check when:
- - create a new one
- - update the name
- - update the project (move to another project)
- """
- data_id = attrs.get("id", None)
- data_name = attrs.get("name", None)
- data_project = attrs.get("project", None)
-
- if self.object:
- data_id = data_id or self.object.id
- data_name = data_name or self.object.name
- data_project = data_project or self.object.project
-
- model = self.Meta.model
- qs = (model.objects.filter(project=data_project, name=data_name)
- .exclude(id=data_id))
- if qs.exists():
- raise ValidationError(_("Already exists one with the same name."))
-
- return attrs
-
- def validate_name(self, attrs, source):
- return self._validate_integrity_between_project_and_name(attrs, source)
-
- def validate_project(self, attrs, source):
- return self._validate_integrity_between_project_and_name(attrs, source)
+class BaseCustomAttributeSerializer(serializers.LightSerializer):
+ name = Field()
+ description = Field()
+ type = Field()
+ order = Field()
+ project = Field(attr="project_id")
+ created_date = Field()
+ modified_date = Field()
class UserStoryCustomAttributeSerializer(BaseCustomAttributeSerializer):
- class Meta(BaseCustomAttributeSerializer.Meta):
- model = models.UserStoryCustomAttribute
+ pass
class TaskCustomAttributeSerializer(BaseCustomAttributeSerializer):
- class Meta(BaseCustomAttributeSerializer.Meta):
- model = models.TaskCustomAttribute
+ pass
class IssueCustomAttributeSerializer(BaseCustomAttributeSerializer):
- class Meta(BaseCustomAttributeSerializer.Meta):
- model = models.IssueCustomAttribute
+ pass
######################################################
# Custom Attribute Serializer
#######################################################
-
-
-class BaseCustomAttributesValuesSerializer(ModelSerializer):
- attributes_values = JsonField(source="attributes_values", label="attributes values")
- _custom_attribute_model = None
- _container_field = None
-
- class Meta:
- exclude = ("id",)
-
- def validate_attributes_values(self, attrs, source):
- # values must be a dict
- data_values = attrs.get("attributes_values", None)
- if self.object:
- data_values = (data_values or self.object.attributes_values)
-
- if type(data_values) is not dict:
- raise ValidationError(_("Invalid content. It must be {\"key\": \"value\",...}"))
-
- # Values keys must be in the container object project
- data_container = attrs.get(self._container_field, None)
- if data_container:
- project_id = data_container.project_id
- elif self.object:
- project_id = getattr(self.object, self._container_field).project_id
- else:
- project_id = None
-
- values_ids = list(data_values.keys())
- qs = self._custom_attribute_model.objects.filter(project=project_id,
- id__in=values_ids)
- if qs.count() != len(values_ids):
- raise ValidationError(_("It contain invalid custom fields."))
-
- return attrs
+class BaseCustomAttributesValuesSerializer(serializers.LightSerializer):
+ attributes_values = Field()
+ version = Field()
class UserStoryCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer):
- _custom_attribute_model = models.UserStoryCustomAttribute
- _container_model = "userstories.UserStory"
- _container_field = "user_story"
-
- class Meta(BaseCustomAttributesValuesSerializer.Meta):
- model = models.UserStoryCustomAttributesValues
+ user_story = Field(attr="user_story.id")
-class TaskCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer, ModelSerializer):
- _custom_attribute_model = models.TaskCustomAttribute
- _container_field = "task"
-
- class Meta(BaseCustomAttributesValuesSerializer.Meta):
- model = models.TaskCustomAttributesValues
+class TaskCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer):
+ task = Field(attr="task.id")
-class IssueCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer, ModelSerializer):
- _custom_attribute_model = models.IssueCustomAttribute
- _container_field = "issue"
-
- class Meta(BaseCustomAttributesValuesSerializer.Meta):
- model = models.IssueCustomAttributesValues
+class IssueCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer):
+ issue = Field(attr="issue.id")
diff --git a/taiga/projects/custom_attributes/validators.py b/taiga/projects/custom_attributes/validators.py
new file mode 100644
index 00000000..506c040c
--- /dev/null
+++ b/taiga/projects/custom_attributes/validators.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014-2016 Andrey Antukh
+# Copyright (C) 2014-2016 Jesús Espino
+# Copyright (C) 2014-2016 David Barragán
+# Copyright (C) 2014-2016 Alejandro Alonso
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+
+from django.utils.translation import ugettext_lazy as _
+
+from taiga.base.fields import JsonField
+from taiga.base.api.serializers import ValidationError
+from taiga.base.api.validators import ModelValidator
+
+from . import models
+
+
+######################################################
+# Custom Attribute Validator
+#######################################################
+
+class BaseCustomAttributeValidator(ModelValidator):
+ class Meta:
+ read_only_fields = ('id',)
+ exclude = ('created_date', 'modified_date')
+
+ def _validate_integrity_between_project_and_name(self, attrs, source):
+ """
+ Check the name is not duplicated in the project. Check when:
+ - create a new one
+ - update the name
+ - update the project (move to another project)
+ """
+ data_id = attrs.get("id", None)
+ data_name = attrs.get("name", None)
+ data_project = attrs.get("project", None)
+
+ if self.object:
+ data_id = data_id or self.object.id
+ data_name = data_name or self.object.name
+ data_project = data_project or self.object.project
+
+ model = self.Meta.model
+ qs = (model.objects.filter(project=data_project, name=data_name)
+ .exclude(id=data_id))
+ if qs.exists():
+ raise ValidationError(_("Already exists one with the same name."))
+
+ return attrs
+
+ def validate_name(self, attrs, source):
+ return self._validate_integrity_between_project_and_name(attrs, source)
+
+ def validate_project(self, attrs, source):
+ return self._validate_integrity_between_project_and_name(attrs, source)
+
+
+class UserStoryCustomAttributeValidator(BaseCustomAttributeValidator):
+ class Meta(BaseCustomAttributeValidator.Meta):
+ model = models.UserStoryCustomAttribute
+
+
+class TaskCustomAttributeValidator(BaseCustomAttributeValidator):
+ class Meta(BaseCustomAttributeValidator.Meta):
+ model = models.TaskCustomAttribute
+
+
+class IssueCustomAttributeValidator(BaseCustomAttributeValidator):
+ class Meta(BaseCustomAttributeValidator.Meta):
+ model = models.IssueCustomAttribute
+
+
+######################################################
+# Custom Attribute Validator
+#######################################################
+
+
+class BaseCustomAttributesValuesValidator(ModelValidator):
+ attributes_values = JsonField(source="attributes_values", label="attributes values")
+ _custom_attribute_model = None
+ _container_field = None
+
+ class Meta:
+ exclude = ("id",)
+
+ def validate_attributes_values(self, attrs, source):
+ # values must be a dict
+ data_values = attrs.get("attributes_values", None)
+ if self.object:
+ data_values = (data_values or self.object.attributes_values)
+
+ if type(data_values) is not dict:
+ raise ValidationError(_("Invalid content. It must be {\"key\": \"value\",...}"))
+
+ # Values keys must be in the container object project
+ data_container = attrs.get(self._container_field, None)
+ if data_container:
+ project_id = data_container.project_id
+ elif self.object:
+ project_id = getattr(self.object, self._container_field).project_id
+ else:
+ project_id = None
+
+ values_ids = list(data_values.keys())
+ qs = self._custom_attribute_model.objects.filter(project=project_id,
+ id__in=values_ids)
+ if qs.count() != len(values_ids):
+ raise ValidationError(_("It contain invalid custom fields."))
+
+ return attrs
+
+
+class UserStoryCustomAttributesValuesValidator(BaseCustomAttributesValuesValidator):
+ _custom_attribute_model = models.UserStoryCustomAttribute
+ _container_model = "userstories.UserStory"
+ _container_field = "user_story"
+
+ class Meta(BaseCustomAttributesValuesValidator.Meta):
+ model = models.UserStoryCustomAttributesValues
+
+
+class TaskCustomAttributesValuesValidator(BaseCustomAttributesValuesValidator, ModelValidator):
+ _custom_attribute_model = models.TaskCustomAttribute
+ _container_field = "task"
+
+ class Meta(BaseCustomAttributesValuesValidator.Meta):
+ model = models.TaskCustomAttributesValues
+
+
+class IssueCustomAttributesValuesValidator(BaseCustomAttributesValuesValidator, ModelValidator):
+ _custom_attribute_model = models.IssueCustomAttribute
+ _container_field = "issue"
+
+ class Meta(BaseCustomAttributesValuesValidator.Meta):
+ model = models.IssueCustomAttributesValues