diff --git a/apps/common/fields/serializer.py b/apps/common/fields/serializer.py index dc7f43428..8f0ae1826 100644 --- a/apps/common/fields/serializer.py +++ b/apps/common/fields/serializer.py @@ -1,13 +1,16 @@ # -*- coding: utf-8 -*- # +import copy +from collections import OrderedDict +from rest_framework.serializers import ALL_FIELDS from rest_framework import serializers import six __all__ = [ 'StringIDField', 'StringManyToManyField', 'ChoiceDisplayField', - 'CustomMetaDictField', 'ReadableHiddenField', + 'CustomMetaDictField', 'ReadableHiddenField', 'JSONFieldModelSerializer' ] @@ -130,3 +133,184 @@ class CustomMetaDictField(serializers.DictField): value = self.filter_value_key(dictionary, value) value = self.strip_value(value) return value + + +class JSONFieldModelSerializer(serializers.Serializer): + """ Model JSONField Serializer""" + + def __init__(self, *args, **kwargs): + mode_field = getattr(self.Meta, 'model_field') + if mode_field: + kwargs['label'] = mode_field.field.verbose_name + super().__init__(*args, **kwargs) + + class Meta: + model = None + model_field = None + fields = None + exclude = None + + def get_fields(self): + assert hasattr(self, 'Meta'), ( + 'Class {serializer_class} missing "Meta" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + assert hasattr(self.Meta, 'model'), ( + 'Class {serializer_class} missing "Meta.model" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + model_fields_mapping = {field.name: field for field in self.Meta.model._meta.fields} + assert hasattr(self.Meta, 'model_field'), ( + 'Class {serializer_class} missing "Meta.model_field" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + + assert self.Meta.model_field.field.name in model_fields_mapping.keys(), ( + 'Class {serializer_class} "Meta.model_field" attribute not in ' + '"Meta.model._meta.fields"'.format( + serializer_class=self.__class__.__name__, + ) + ) + + declared_fields = copy.deepcopy(self._declared_fields) + + read_only_field_names = self.get_read_only_field_names() + + field_names = self.get_field_names(declared_fields) + + fields = OrderedDict() + for field_name in field_names: + if field_name not in declared_fields: + continue + field = declared_fields[field_name] + if field_name in read_only_field_names: + setattr(field, 'read_only', True) + fields[field_name] = field + return fields + + def get_field_names(self, declared_fields): + """ + Returns the list of all field names that should be created when + instantiating this serializer class. This is based on the default + set of fields, but also takes into account the `Meta.fields` or + `Meta.exclude` options if they have been specified. + """ + + fields = getattr(self.Meta, 'fields', None) + exclude = getattr(self.Meta, 'exclude', None) + + if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)): + raise TypeError( + 'The `fields` option must be a list or tuple or "__all__". ' + 'Got %s.' % type(fields).__name__ + ) + + if exclude and not isinstance(exclude, (list, tuple)): + raise TypeError( + 'The `exclude` option must be a list or tuple. Got %s.' % + type(exclude).__name__ + ) + + assert not (fields and exclude), ( + "Cannot set both 'fields' and 'exclude' options on " + "serializer {serializer_class}.".format( + serializer_class=self.__class__.__name__ + ) + ) + + assert not (fields is None and exclude is None), ( + "Creating a ModelSerializer without either the 'fields' attribute " + "or the 'exclude' attribute has been deprecated since 3.3.0, " + "and is now disallowed. Add an explicit fields = '__all__' to the " + "{serializer_class} serializer.".format( + serializer_class=self.__class__.__name__ + ), + ) + + if fields == ALL_FIELDS: + fields = None + + if fields is not None: + # Ensure that all declared fields have also been included in the + # `Meta.fields` option. + + # Do not require any fields that are declared in a parent class, + # in order to allow serializer subclasses to only include + # a subset of fields. + required_field_names = set(declared_fields) + for cls in self.__class__.__bases__: + required_field_names -= set(getattr(cls, '_declared_fields', [])) + + for field_name in required_field_names: + assert field_name in fields, ( + "The field '{field_name}' was declared on serializer " + "{serializer_class}, but has not been included in the " + "'fields' option.".format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + return fields + + # Use the default set of field names if `Meta.fields` is not specified. + fields = self.get_default_field_names(declared_fields) + + if exclude is not None: + # If `Meta.exclude` is included, then remove those fields. + for field_name in exclude: + assert field_name not in self._declared_fields, ( + "Cannot both declare the field '{field_name}' and include " + "it in the {serializer_class} 'exclude' option. Remove the " + "field or, if inherited from a parent serializer, disable " + "with `{field_name} = None`." + .format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + + assert field_name in fields, ( + "The field '{field_name}' was included on serializer " + "{serializer_class} in the 'exclude' option, but does " + "not match any model field.".format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + fields.remove(field_name) + return fields + + @staticmethod + def get_default_field_names(declared_fields): + return declared_fields + + def get_read_only_field_names(self): + read_only_fields = getattr(self.Meta, 'read_only_fields', None) + if read_only_fields is not None: + if not isinstance(read_only_fields, (list, tuple)): + raise TypeError( + 'The `read_only_fields` option must be a list or tuple. ' + 'Got %s.' % type(read_only_fields).__name__ + ) + return read_only_fields + + def to_internal_value(self, data): + return super().to_internal_value(data) + + def to_representation(self, instance): + if not isinstance(instance, dict): + return super().to_representation(instance) + for field_name, field in self.fields.items(): + if field_name in instance: + continue + if field.allow_null: + continue + setattr(field, 'allow_null', True) + return super().to_representation(instance) + + + + diff --git a/apps/common/mixins/api.py b/apps/common/mixins/api.py index 3b0b4b753..69e346bee 100644 --- a/apps/common/mixins/api.py +++ b/apps/common/mixins/api.py @@ -56,7 +56,7 @@ class GenericSerializerMixin: return serializer_class -class JSONFieldsSerializerMixin: +class JSONFieldsModelSerializerMixin: """ 作用: 获取包含 JSONField 字段的序列类 @@ -76,31 +76,20 @@ class JSONFieldsSerializerMixin: 'json_field_1': { 'type': { 'apply_asset': { - 'get': { - 'class': TestSerializer, - 'attrs': {'required': True}, - }, - 'post': { - 'class': TestSerializer, - 'attrs': {'required': True} - }, - 'open': { - 'class': TestSerializer, - 'attrs': {'required': False} - }, - 'approve': { - 'class': TestSerializer, - }, + 'get': TestSerializer, + 'post': TestSerializer, + 'open':TestSerializer, + 'approve': TestSerializer, }, 'apply_application': { - 'get': {}, - 'post': {}, - 'put': {}, + 'get': TestSerializer, + 'post': TestSerializer, + 'put': TestSerializer, }, 'login_confirm': { - 'get': {}, - 'post': {}, - 'put': {}, + 'get': TestSerializer, + 'post': TestSerializer, + 'put': TestSerializer, } }, 'category': {} @@ -113,19 +102,13 @@ class JSONFieldsSerializerMixin: json_fields_category_mapping = {} json_fields_serializer_classes = None - @lazyproperty - def default_json_field_serializer(self): - class DefaultJSONFieldSerializer(serializers.JSONField): - pass - if self.action in ['get', 'list', 'retrieve']: - attrs = {'readonly': False} - else: - attrs = {'readonly': True} - return DefaultJSONFieldSerializer(attrs) + # 保存当前处理的JSONField名称 + __field = None + serializer_class = None - def get_json_field_query_category(self, field, category): + def get_json_field_query_category(self, category): query_category = self.request.query_params.get(category) - category_choices = self.json_fields_category_mapping[field][category] + category_choices = self.json_fields_category_mapping[self.__field][category] if query_category and query_category not in category_choices: error = _( 'Please bring the query parameter `{}`, ' @@ -135,56 +118,57 @@ class JSONFieldsSerializerMixin: raise JMSException({'query_params_error': error}) return query_category - def get_json_field_serializer_classes_by_query_category(self, field): - serializer_classes = None - category_collection = self.json_fields_category_mapping[field] + def get_json_field_action_serializer_classes_by_query_category(self): + action_serializer_classes = None + category_collection = self.json_fields_category_mapping[self.__field] for category in category_collection: - query_category = self.get_json_field_query_category(field, category) - if not query_category: + category_value = self.get_json_field_query_category(category) + if not category_value: continue - category_serializer_classes = self.json_fields_serializer_classes[field][category] - if query_category not in category_serializer_classes.keys(): - continue - serializer_classes = category_serializer_classes[query_category] + category_serializer_classes = self.json_fields_serializer_classes[self.__field][category] + action_serializer_classes = category_serializer_classes.get(category_value) + if action_serializer_classes: + break + return action_serializer_classes + def get_json_field_action_serializer_classes(self): + category_collection = self.json_fields_category_mapping[self.__field] + if category_collection: + serializer_classes = self.get_json_field_action_serializer_classes_by_query_category() + else: + serializer_classes = self.json_fields_serializer_classes[self.__field] return serializer_classes - def get_json_field_serializer_info_by_view_action(self, serializer_classes): + def get_json_field_serializer_class_by_action(self, serializer_classes): + if serializer_classes is None: + return None if self.action in ['metadata']: action = self.request.query_params.get('action') if not action: raise JMSException('The `metadata` methods must carry query parameter `action`') else: action = self.action - serializer_info = serializer_classes.get(action) - return serializer_info + serializer_class = serializer_classes.get(action) + return serializer_class - def get_json_field_serializer_info(self, field): - category_collection = self.json_fields_category_mapping[field] - if category_collection: - serializer_classes = self.get_json_field_serializer_classes_by_query_category(field) - else: - serializer_classes = self.json_fields_serializer_classes[field] + @lazyproperty + def default_json_field_serializer_class(self): + readonly_json_field_serializer_class = type( + 'DefaultReadonlyJSONFieldSerializer', (serializers.JSONField,), + ) + return readonly_json_field_serializer_class - if not serializer_classes: - return None - - serializer_info = self.get_json_field_serializer_info_by_view_action(serializer_classes) - return serializer_info - - @staticmethod - def new_json_field_serializer(class_info): - serializer_class = class_info['class'] - serializer_attrs = class_info.get('attrs', {}) - serializer = serializer_class(serializer_attrs) - return serializer - - def get_json_field_serializer(self, field): - serializer_info = self.get_json_field_serializer_info(field) - if not serializer_info: - return None - serializer = self.new_json_field_serializer(serializer_info) - return serializer + def get_json_field_serializer(self): + serializer_classes = self.get_json_field_action_serializer_classes() + serializer_class = self.get_json_field_serializer_class_by_action(serializer_classes) + if serializer_class: + serializer = serializer_class() + return serializer + serializer_class = serializer_classes.get('default') + if serializer_class: + serializer = serializer_class(**{'read_only': True}) + return serializer + return self.default_json_field_serializer_class(**{'read_only': True}) def get_json_fields_serializer_mapping(self): """ @@ -196,14 +180,12 @@ class JSONFieldsSerializerMixin: fields_serializer_mapping = {} fields = self.json_fields_serializer_classes.keys() for field in fields: - serializer = self.get_json_field_serializer(field) - if serializer is None: - serializer = self.default_json_field_serializer - fields_serializer_mapping[field] = serializer + self.__field = field + serializer = self.get_json_field_serializer() + fields_serializer_mapping[self.__field] = serializer return fields_serializer_mapping - @staticmethod - def new_include_json_fields_serializer_class(base, attrs): + def build_include_json_fields_serializer_class(self, base, attrs): serializer_class_name = ''.join([ field_serializer.__class__.__name__ for field_serializer in attrs.values() ]) @@ -217,13 +199,13 @@ class JSONFieldsSerializerMixin: fields_serializer_mapping = self.get_json_fields_serializer_mapping() if not fields_serializer_mapping: return serializer_class - serializer_class = self.new_include_json_fields_serializer_class( + serializer_class = self.build_include_json_fields_serializer_class( base=serializer_class, attrs=fields_serializer_mapping ) return serializer_class -class SerializerMixin(JSONFieldsSerializerMixin, GenericSerializerMixin): +class SerializerMixin(JSONFieldsModelSerializerMixin, GenericSerializerMixin): pass diff --git a/apps/tickets/api/ticket/mixin.py b/apps/tickets/api/ticket/mixin.py index 0a3598ccf..21067614d 100644 --- a/apps/tickets/api/ticket/mixin.py +++ b/apps/tickets/api/ticket/mixin.py @@ -1,11 +1,10 @@ -from common.exceptions import JMSException from tickets import const, serializers -__all__ = ['TicketJSONFieldsSerializerViewMixin'] +__all__ = ['TicketJSONFieldsModelSerializerViewMixin'] -class TicketJSONFieldsSerializerViewMixin: +class TicketJSONFieldsModelSerializerViewMixin: json_fields_category_mapping = { 'meta': { 'type': const.TicketTypeChoices.values, @@ -15,30 +14,18 @@ class TicketJSONFieldsSerializerViewMixin: 'meta': { 'type': { const.TicketTypeChoices.apply_asset.value: { - 'open': { - 'class': serializers.TicketMetaApplyAssetApplySerializer, - 'attrs': {'required': True} - }, - 'approve': { - 'class': serializers.TicketMetaApplyAssetApproveSerializer, - 'attrs': {'required': True} - } + 'default': serializers.TicketMetaApplyAssetSerializer, + 'open': serializers.TicketMetaApplyAssetApplySerializer, + 'approve': serializers.TicketMetaApplyAssetApproveSerializer }, const.TicketTypeChoices.apply_application.value: { - 'open': { - 'class': serializers.TicketMetaApplyApplicationApplySerializer, - 'attrs': {'required': True} - }, - 'approve': { - 'class': serializers.TicketMetaApplyApplicationApproveSerializer, - 'attrs': {'required': True} - } + 'default': serializers.TicketMetaApplyApplicationSerializer, + 'open': serializers.TicketMetaApplyApplicationApplySerializer, + 'approve': serializers.TicketMetaApplyApplicationApproveSerializer, }, const.TicketTypeChoices.login_confirm.value: { - 'open': { - 'class': serializers.TicketMetaLoginConfirmApplySerializer, - 'attrs': {'required': True} - } + 'default': serializers.TicketMetaLoginConfirmSerializer, + 'open': serializers.TicketMetaLoginConfirmApplySerializer } } } diff --git a/apps/tickets/api/ticket/ticket.py b/apps/tickets/api/ticket/ticket.py index daa24a364..2f9497522 100644 --- a/apps/tickets/api/ticket/ticket.py +++ b/apps/tickets/api/ticket/ticket.py @@ -11,13 +11,13 @@ from common.const.http import POST, PUT from tickets import serializers from tickets.permissions.ticket import IsAssignee, NotClosed from tickets.models import Ticket -from tickets.api.ticket.mixin import TicketJSONFieldsSerializerViewMixin +from tickets.api.ticket.mixin import TicketJSONFieldsModelSerializerViewMixin __all__ = ['TicketViewSet'] -class TicketViewSet(TicketJSONFieldsSerializerViewMixin, CommonApiMixin, viewsets.ModelViewSet): +class TicketViewSet(TicketJSONFieldsModelSerializerViewMixin, CommonApiMixin, viewsets.ModelViewSet): permission_classes = (IsValidUser,) serializer_class = serializers.TicketSerializer serializer_classes = { diff --git a/apps/tickets/serializers/ticket/meta/apply_application.py b/apps/tickets/serializers/ticket/meta/apply_application.py index 531d74279..f316074d5 100644 --- a/apps/tickets/serializers/ticket/meta/apply_application.py +++ b/apps/tickets/serializers/ticket/meta/apply_application.py @@ -3,15 +3,18 @@ from django.utils.translation import ugettext_lazy as _ from applications.models import Category, Application from assets.models import SystemUser -from .base import BaseTicketMetaSerializer, BaseTicketMetaApproveSerializerMixin +from .base import BaseTicketMetaApproveSerializerMixin +from common.fields.serializer import JSONFieldModelSerializer +from tickets.models import Ticket __all__ = [ + 'TicketMetaApplyApplicationSerializer', 'TicketMetaApplyApplicationApplySerializer', 'TicketMetaApplyApplicationApproveSerializer', ] -class TicketMetaApplyApplicationSerializer(BaseTicketMetaSerializer): +class TicketMetaApplyApplicationSerializer(JSONFieldModelSerializer): # 申请信息 apply_category = serializers.ChoiceField( required=True, choices=Category.choices, label=_('Category') @@ -63,16 +66,33 @@ class TicketMetaApplyApplicationSerializer(BaseTicketMetaSerializer): required=True, label=_('Date expired') ) - -class TicketMetaApplyApplicationApplySerializer(TicketMetaApplyApplicationSerializer): - class Meta: + model = Ticket + model_field = Ticket.meta fields = [ 'apply_category', 'apply_category_display', 'apply_type', 'apply_type_display', 'apply_application_group', 'apply_system_user_group', 'apply_date_start', 'apply_date_expired', + + 'approve_applications', 'approve_applications_snapshot', + 'approve_system_users', 'approve_system_users_snapshot', + 'approve_date_start', 'approve_date_expired' ] + read_only_fields = fields + + +class TicketMetaApplyApplicationApplySerializer(TicketMetaApplyApplicationSerializer): + + class Meta(TicketMetaApplyApplicationSerializer.Meta): + required_fields = [ + 'apply_category', 'apply_type', + 'apply_application_group', 'apply_system_user_group', + 'apply_date_start', 'apply_date_expired', + ] + read_only_fields = list( + set(TicketMetaApplyApplicationSerializer.Meta.fields) - set(required_fields) + ) def validate_apply_type(self, tp): category = self.root.initial_data['meta'].get('apply_category') @@ -89,11 +109,13 @@ class TicketMetaApplyApplicationApproveSerializer(BaseTicketMetaApproveSerialize TicketMetaApplyApplicationSerializer): class Meta: - fields = { - 'approve_applications', 'approve_applications_snapshot', - 'approve_system_users', 'approve_system_users_snapshot', + required_fields = { + 'approve_applications', 'approve_system_users', 'approve_date_start', 'approve_date_expired' } + read_only_fields = list( + set(TicketMetaApplyApplicationSerializer.Meta.fields) - set(required_fields) + ) def validate_approve_applications(self, approve_applications): application_type = self.root.instance.meta['apply_type'] diff --git a/apps/tickets/serializers/ticket/meta/apply_asset.py b/apps/tickets/serializers/ticket/meta/apply_asset.py index 4a78f374d..3aea29081 100644 --- a/apps/tickets/serializers/ticket/meta/apply_asset.py +++ b/apps/tickets/serializers/ticket/meta/apply_asset.py @@ -2,16 +2,20 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from perms.serializers import ActionsField from assets.models import Asset, SystemUser -from .base import BaseTicketMetaSerializer, BaseTicketMetaApproveSerializerMixin +from .base import BaseTicketMetaApproveSerializerMixin +from tickets.models import Ticket + +from common.fields.serializer import JSONFieldModelSerializer __all__ = [ + 'TicketMetaApplyAssetSerializer', 'TicketMetaApplyAssetApplySerializer', 'TicketMetaApplyAssetApproveSerializer', ] -class TicketMetaApplyAssetSerializer(BaseTicketMetaSerializer): +class TicketMetaApplyAssetSerializer(JSONFieldModelSerializer): # 申请信息 apply_ip_group = serializers.ListField( required=False, child=serializers.IPAddressField(), label=_('IP group'), @@ -71,28 +75,46 @@ class TicketMetaApplyAssetSerializer(BaseTicketMetaSerializer): required=True, label=_('Date expired') ) - -class TicketMetaApplyAssetApplySerializer(TicketMetaApplyAssetSerializer): - class Meta: + model = Ticket + model_field = Ticket.meta fields = [ 'apply_ip_group', 'apply_hostname_group', 'apply_system_user_group', 'apply_actions', 'apply_actions_display', 'apply_date_start', 'apply_date_expired', - ] - -class TicketMetaApplyAssetApproveSerializer(BaseTicketMetaApproveSerializerMixin, - TicketMetaApplyAssetSerializer): - - class Meta: - fields = [ 'approve_assets', 'approve_assets_snapshot', 'approve_system_users', 'approve_system_users_snapshot', 'approve_actions', 'approve_actions_display', 'approve_date_start', 'approve_date_expired', ] + read_only_fields = fields + + +class TicketMetaApplyAssetApplySerializer(TicketMetaApplyAssetSerializer): + + class Meta(TicketMetaApplyAssetSerializer.Meta): + required_fields = [ + 'apply_ip_group', 'apply_hostname_group', 'apply_system_user_group', + 'apply_actions', 'apply_date_start', 'apply_date_expired', + ] + read_only_fields = list( + set(TicketMetaApplyAssetSerializer.Meta.fields) - set(required_fields) + ) + + +class TicketMetaApplyAssetApproveSerializer(BaseTicketMetaApproveSerializerMixin, + TicketMetaApplyAssetSerializer): + + class Meta(TicketMetaApplyAssetSerializer.Meta): + required_fields = [ + 'approve_assets', 'approve_system_users', 'approve_actions', + 'approve_date_start', 'approve_date_expired', + ] + read_only_fields = list( + set(TicketMetaApplyAssetSerializer.Meta.fields) - set(required_fields) + ) def validate_approve_assets(self, approve_assets): assets_id = self.filter_approve_resources(resource_model=Asset, resources_id=approve_assets) diff --git a/apps/tickets/serializers/ticket/meta/base.py b/apps/tickets/serializers/ticket/meta/base.py index 8bfec3a50..6dd3f0e7b 100644 --- a/apps/tickets/serializers/ticket/meta/base.py +++ b/apps/tickets/serializers/ticket/meta/base.py @@ -1,29 +1,13 @@ +import copy from collections import OrderedDict from rest_framework import serializers +from rest_framework.serializers import ALL_FIELDS from django.utils.translation import ugettext_lazy as _ from orgs.utils import tmp_to_org from assets.models import SystemUser -class BaseTicketMetaSerializer(serializers.Serializer): - - class Meta: - fields = '__all__' - - def get_fields(self): - fields = super().get_fields() - required_fields = self.Meta.fields - if required_fields == '__all__': - return fields - - fields = OrderedDict({ - field_name: fields.pop(field_name) for field_name in set(required_fields) - if field_name in fields.keys() - }) - return fields - - class BaseTicketMetaApproveSerializerMixin: def _filter_approve_resources_by_org(self, model, resources_id): diff --git a/apps/tickets/serializers/ticket/meta/login_confirm.py b/apps/tickets/serializers/ticket/meta/login_confirm.py index 205112b81..1dcb8ee0b 100644 --- a/apps/tickets/serializers/ticket/meta/login_confirm.py +++ b/apps/tickets/serializers/ticket/meta/login_confirm.py @@ -1,14 +1,15 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ -from .base import BaseTicketMetaSerializer +from common.fields.serializer import JSONFieldModelSerializer +from tickets.models import Ticket __all__ = [ - 'TicketMetaLoginConfirmApplySerializer', + 'TicketMetaLoginConfirmSerializer', 'TicketMetaLoginConfirmApplySerializer', ] -class TicketMetaLoginConfirmSerializer(BaseTicketMetaSerializer): +class TicketMetaLoginConfirmSerializer(JSONFieldModelSerializer): apply_login_ip = serializers.IPAddressField( required=True, label=_('Login ip') ) @@ -19,6 +20,21 @@ class TicketMetaLoginConfirmSerializer(BaseTicketMetaSerializer): required=True, label=_('Login datetime') ) + class Meta: + model = Ticket + model_field = Ticket.meta + fields = [ + 'apply_login_ip', 'apply_login_city', 'apply_login_datetime' + ] + read_only_fields = fields + class TicketMetaLoginConfirmApplySerializer(TicketMetaLoginConfirmSerializer): - pass + + class Meta(TicketMetaLoginConfirmSerializer.Meta): + required_fields = [ + 'apply_login_ip', 'apply_login_city', 'apply_login_datetime' + ] + read_only_fields = list( + set(TicketMetaLoginConfirmSerializer.Meta.fields) - set(required_fields) + ) diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index d430919b1..c5c489244 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -92,8 +92,7 @@ class TicketApplySerializer(TicketActionSerializer): raise serializers.ValidationError(error) return valid_assignees - @staticmethod - def validate_action(action): + def validate_action(self, action): return const.TicketActionChoices.open.value @@ -123,15 +122,13 @@ class TicketApproveSerializer(TicketProcessSerializer): class TicketRejectSerializer(TicketProcessSerializer): - @staticmethod - def validate_action(action): + def validate_action(self, action): return const.TicketActionChoices.reject.value class TicketCloseSerializer(TicketProcessSerializer): - @staticmethod - def validate_action(action): + def validate_action(self, action): return const.TicketActionChoices.close.value