mirror of https://github.com/jumpserver/jumpserver
feat: 实现MethodSerializer, 满足serializer中SerializerField动态更改的需求 (#5382)
* feat: 实现MethodSerializer, 满足serializer中SerializerField动态更改的需求 * feat: 实现MethodSerializer, 满足serializer中SerializerField动态更改的需求 (2) Co-authored-by: Bai <bugatti_it@163.com>pull/5383/head
parent
17a01a12db
commit
7167515a53
|
@ -4,35 +4,36 @@
|
|||
from rest_framework import serializers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from common.drf.serializers import DynamicMappingSerializer
|
||||
from .attrs import attrs_field_dynamic_mapping_serializers
|
||||
from common.drf.serializers import MethodSerializer
|
||||
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
|
||||
|
||||
from .. import models
|
||||
|
||||
__all__ = [
|
||||
'ApplicationSerializer',
|
||||
'IncludeDynamicMappingSerializerFieldApplicationSerializerMixin',
|
||||
'ApplicationSerializer', 'ApplicationSerializerMixin',
|
||||
]
|
||||
|
||||
|
||||
class IncludeDynamicMappingSerializerFieldApplicationSerializerMixin(serializers.Serializer):
|
||||
attrs = DynamicMappingSerializer(mapping_serializers=attrs_field_dynamic_mapping_serializers)
|
||||
class ApplicationSerializerMixin(serializers.Serializer):
|
||||
attrs = MethodSerializer()
|
||||
|
||||
def get_attrs_mapping_path(self, mapping_serializers):
|
||||
def get_attrs_serializer(self):
|
||||
request = self.context['request']
|
||||
query_type = request.query_params.get('type')
|
||||
query_category = request.query_params.get('category')
|
||||
if query_type:
|
||||
mapping_path = ['type', query_type]
|
||||
serializer_class = type_serializer_classes_mapping.get(query_type)
|
||||
elif query_category:
|
||||
mapping_path = ['category', query_category]
|
||||
serializer_class = category_serializer_classes_mapping.get(query_category)
|
||||
else:
|
||||
mapping_path = ['default']
|
||||
return mapping_path
|
||||
serializer_class = None
|
||||
if serializer_class is None:
|
||||
serializer_class = serializers.Serializer
|
||||
serializer = serializer_class()
|
||||
return serializer
|
||||
|
||||
|
||||
class ApplicationSerializer(IncludeDynamicMappingSerializerFieldApplicationSerializerMixin,
|
||||
BulkOrgResourceModelSerializer):
|
||||
class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSerializer):
|
||||
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category'))
|
||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
||||
|
||||
|
|
|
@ -4,62 +4,39 @@ from . import application_category, application_type
|
|||
|
||||
|
||||
__all__ = [
|
||||
'attrs_field_dynamic_mapping_serializers',
|
||||
'category_serializer_classes_mapping',
|
||||
'type_serializer_classes_mapping',
|
||||
'get_serializer_class_by_application_type',
|
||||
]
|
||||
|
||||
|
||||
# application category
|
||||
# --------------------
|
||||
# define `attrs` field `category serializers mapping`
|
||||
# ---------------------------------------------------
|
||||
|
||||
category_db = const.ApplicationCategoryChoices.db.value
|
||||
category_remote_app = const.ApplicationCategoryChoices.remote_app.value
|
||||
category_cloud = const.ApplicationCategoryChoices.cloud.value
|
||||
category_serializer_classes_mapping = {
|
||||
const.ApplicationCategoryChoices.db.value: application_category.DBSerializer,
|
||||
const.ApplicationCategoryChoices.remote_app.value: application_category.RemoteAppSerializer,
|
||||
const.ApplicationCategoryChoices.cloud.value: application_category.CloudSerializer,
|
||||
}
|
||||
|
||||
# define `attrs` field `type serializers mapping`
|
||||
# -----------------------------------------------
|
||||
|
||||
# application type
|
||||
# ----------------
|
||||
|
||||
# db
|
||||
type_mysql = const.ApplicationTypeChoices.mysql.value
|
||||
type_mariadb = const.ApplicationTypeChoices.mariadb.value
|
||||
type_oracle = const.ApplicationTypeChoices.oracle.value
|
||||
type_pgsql = const.ApplicationTypeChoices.pgsql.value
|
||||
# remote-app
|
||||
type_chrome = const.ApplicationTypeChoices.chrome.value
|
||||
type_mysql_workbench = const.ApplicationTypeChoices.mysql_workbench.value
|
||||
type_vmware_client = const.ApplicationTypeChoices.vmware_client.value
|
||||
type_custom = const.ApplicationTypeChoices.custom.value
|
||||
# cloud
|
||||
type_k8s = const.ApplicationTypeChoices.k8s.value
|
||||
|
||||
|
||||
# define `attrs` field `dynamic mapping serializers`
|
||||
# --------------------------------------------------
|
||||
|
||||
|
||||
attrs_field_dynamic_mapping_serializers = {
|
||||
'category': {
|
||||
category_db: application_category.DBSerializer,
|
||||
category_remote_app: application_category.RemoteAppSerializer,
|
||||
category_cloud: application_category.CloudSerializer,
|
||||
},
|
||||
'type': {
|
||||
# db
|
||||
type_mysql: application_type.MySQLSerializer,
|
||||
type_mariadb: application_type.MariaDBSerializer,
|
||||
type_oracle: application_type.OracleSerializer,
|
||||
type_pgsql: application_type.PostgreSerializer,
|
||||
# remote-app
|
||||
type_chrome: application_type.ChromeSerializer,
|
||||
type_mysql_workbench: application_type.MySQLWorkbenchSerializer,
|
||||
type_vmware_client: application_type.VMwareClientSerializer,
|
||||
type_custom: application_type.CustomSerializer,
|
||||
# cloud
|
||||
type_k8s: application_type.K8SSerializer
|
||||
}
|
||||
type_serializer_classes_mapping = {
|
||||
# db
|
||||
const.ApplicationTypeChoices.mysql.value: application_type.MySQLSerializer,
|
||||
const.ApplicationTypeChoices.mariadb.value: application_type.MariaDBSerializer,
|
||||
const.ApplicationTypeChoices.oracle.value: application_type.OracleSerializer,
|
||||
const.ApplicationTypeChoices.pgsql.value: application_type.PostgreSerializer,
|
||||
# remote-app
|
||||
const.ApplicationTypeChoices.chrome.value: application_type.ChromeSerializer,
|
||||
const.ApplicationTypeChoices.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
|
||||
const.ApplicationTypeChoices.vmware_client.value: application_type.VMwareClientSerializer,
|
||||
const.ApplicationTypeChoices.custom.value: application_type.CustomSerializer,
|
||||
# cloud
|
||||
const.ApplicationTypeChoices.k8s.value: application_type.K8SSerializer
|
||||
}
|
||||
|
||||
|
||||
def get_serializer_class_by_application_type(_application_type):
|
||||
return attrs_field_dynamic_mapping_serializers['type'].get(_application_type)
|
||||
return type_serializer_classes_mapping.get(_application_type)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import copy
|
||||
from rest_framework import serializers
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
@ -8,97 +7,51 @@ from common.mixins import BulkListSerializerMixin
|
|||
from django.utils.functional import cached_property
|
||||
from rest_framework.utils.serializer_helpers import BindingDict
|
||||
from common.mixins.serializers import BulkSerializerMixin
|
||||
from common.utils import QuickLookupDict
|
||||
|
||||
__all__ = [
|
||||
'DynamicMappingSerializer',
|
||||
'MethodSerializer',
|
||||
'EmptySerializer', 'BulkModelSerializer', 'AdaptedBulkListSerializer', 'CeleryTaskSerializer'
|
||||
]
|
||||
|
||||
|
||||
# DynamicMappingSerializer
|
||||
# ------------------------
|
||||
# MethodSerializer
|
||||
# ----------------
|
||||
|
||||
|
||||
class DynamicMappingSerializer(serializers.Serializer):
|
||||
data_type_error_messages = 'Expect get instance of type `{}`, but got instance type of `{}`'
|
||||
class MethodSerializer(serializers.Serializer):
|
||||
|
||||
def __init__(self, mapping_serializers=None, get_mapping_serializers_method_name=None,
|
||||
get_mapping_path_method_name=None, default_serializer=None, **kwargs):
|
||||
self.mapping_serializers = mapping_serializers
|
||||
self.get_mapping_serializers_method_name = get_mapping_serializers_method_name
|
||||
self.get_mapping_path_method_name = get_mapping_path_method_name
|
||||
self.default_serializer = default_serializer or serializers.Serializer
|
||||
def __init__(self, method_name=None, **kwargs):
|
||||
self.method_name = method_name
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def bind(self, field_name, parent):
|
||||
# The get mapping serializers method name defaults to `get_{field_name}_mapping_serializers`
|
||||
if self.get_mapping_serializers_method_name is None:
|
||||
method_name = 'get_{field_name}_mapping_serializers'.format(field_name=field_name)
|
||||
self.get_mapping_serializers_method_name = method_name
|
||||
|
||||
# The get mapping rule method name defaults to `get_{field_name}_mapping_path`.
|
||||
if self.get_mapping_path_method_name is None:
|
||||
method_name = 'get_{field_name}_mapping_path'.format(field_name=field_name)
|
||||
self.get_mapping_path_method_name = method_name
|
||||
if self.method_name is None:
|
||||
method_name = 'get_{field_name}_serializer'.format(field_name=field_name)
|
||||
self.method_name = method_name
|
||||
|
||||
super().bind(field_name, parent)
|
||||
|
||||
def get_mapping_serializers(self):
|
||||
if self.mapping_serializers is not None:
|
||||
return self.mapping_serializers
|
||||
method = getattr(self.parent, self.get_mapping_serializers_method_name)
|
||||
@cached_property
|
||||
def serializer(self) -> serializers.Serializer:
|
||||
method = getattr(self.parent, self.method_name)
|
||||
return method()
|
||||
|
||||
def get_mapping_path(self, mapping_serializers):
|
||||
method = getattr(self.parent, self.get_mapping_path_method_name)
|
||||
return method(mapping_serializers)
|
||||
|
||||
@staticmethod
|
||||
def mapping(mapping_serializers, mapping_path):
|
||||
quick_lookup_dict = QuickLookupDict(data=mapping_serializers)
|
||||
serializer = quick_lookup_dict.get(key_path=mapping_path)
|
||||
return serializer
|
||||
|
||||
def get_mapped_serializer(self):
|
||||
mapping_serializers = self.get_mapping_serializers()
|
||||
assert isinstance(mapping_serializers, dict), (
|
||||
self.data_type_error_messages.format('dict', type(mapping_serializers))
|
||||
)
|
||||
mapping_path = self.get_mapping_path(mapping_serializers)
|
||||
assert isinstance(mapping_path, list), (
|
||||
self.data_type_error_messages.format('list', type(mapping_path))
|
||||
)
|
||||
serializer = self.mapping(mapping_serializers, mapping_path)
|
||||
return serializer
|
||||
|
||||
@cached_property
|
||||
def mapped_serializer(self):
|
||||
serializer = self.get_mapped_serializer()
|
||||
if serializer is None:
|
||||
serializer = self.default_serializer
|
||||
if isinstance(serializer, type):
|
||||
serializer = serializer()
|
||||
return serializer
|
||||
|
||||
def get_fields(self):
|
||||
fields = self.mapped_serializer.get_fields()
|
||||
return fields
|
||||
return self.serializer.get_fields()
|
||||
|
||||
@cached_property
|
||||
def fields(self):
|
||||
"""
|
||||
重写此方法因为在 BindingDict 中要设置每一个 field 的 parent 为 `mapped_serializer`,
|
||||
重写此方法因为在 BindingDict 中要设置每一个 field 的 parent 为 `serializer`,
|
||||
这样在调用 field.parent 时, 才会达到预期的结果,
|
||||
比如: serializers.SerializerMethodField
|
||||
"""
|
||||
fields = BindingDict(self.mapped_serializer)
|
||||
fields = BindingDict(self.serializer)
|
||||
for key, value in self.get_fields().items():
|
||||
fields[key] = value
|
||||
return fields
|
||||
|
||||
|
||||
#
|
||||
# Other Serializer
|
||||
# ----------------
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from assets.models import SystemUser
|
||||
from applications.models import Application
|
||||
from applications.serializers import IncludeDynamicMappingSerializerFieldApplicationSerializerMixin
|
||||
from applications.serializers import ApplicationSerializerMixin
|
||||
|
||||
__all__ = [
|
||||
'ApplicationGrantedSerializer', 'ApplicationSystemUserSerializer'
|
||||
|
@ -26,8 +26,7 @@ class ApplicationSystemUserSerializer(serializers.ModelSerializer):
|
|||
read_only_fields = fields
|
||||
|
||||
|
||||
class ApplicationGrantedSerializer(IncludeDynamicMappingSerializerFieldApplicationSerializerMixin,
|
||||
serializers.ModelSerializer):
|
||||
class ApplicationGrantedSerializer(ApplicationSerializerMixin, serializers.ModelSerializer):
|
||||
"""
|
||||
被授权应用的数据结构
|
||||
"""
|
||||
|
|
|
@ -2,46 +2,29 @@ from tickets import const
|
|||
from .ticket_type import apply_asset, apply_application, login_confirm
|
||||
|
||||
__all__ = [
|
||||
'meta_field_dynamic_mapping_serializers',
|
||||
'type_serializer_classes_mapping',
|
||||
]
|
||||
|
||||
# ticket type
|
||||
# -----------
|
||||
|
||||
|
||||
type_apply_asset = const.TicketTypeChoices.apply_asset.value
|
||||
type_apply_application = const.TicketTypeChoices.apply_application.value
|
||||
type_login_confirm = const.TicketTypeChoices.login_confirm.value
|
||||
|
||||
# ticket action
|
||||
# -------------
|
||||
|
||||
|
||||
actions = const.TicketActionChoices.values
|
||||
action_open = const.TicketActionChoices.open.value
|
||||
action_approve = const.TicketActionChoices.approve.value
|
||||
action_reject = const.TicketActionChoices.reject.value
|
||||
action_close = const.TicketActionChoices.close.value
|
||||
|
||||
|
||||
# defines `meta` field dynamic mapping serializers
|
||||
# ------------------------------------------------
|
||||
|
||||
|
||||
meta_field_dynamic_mapping_serializers = {
|
||||
'type': {
|
||||
type_apply_asset: {
|
||||
action_open: apply_asset.ApplySerializer,
|
||||
action_approve: apply_asset.ApproveSerializer,
|
||||
},
|
||||
type_apply_application: {
|
||||
action_open: apply_application.ApplySerializer,
|
||||
action_approve: apply_application.ApproveSerializer,
|
||||
},
|
||||
type_login_confirm: {
|
||||
action_open: login_confirm.ApplySerializer,
|
||||
}
|
||||
type_serializer_classes_mapping = {
|
||||
const.TicketTypeChoices.apply_asset.value: {
|
||||
action_open: apply_asset.ApplySerializer,
|
||||
action_approve: apply_asset.ApproveSerializer,
|
||||
},
|
||||
const.TicketTypeChoices.apply_application.value: {
|
||||
action_open: apply_application.ApplySerializer,
|
||||
action_approve: apply_application.ApproveSerializer,
|
||||
},
|
||||
const.TicketTypeChoices.login_confirm.value: {
|
||||
action_open: login_confirm.ApplySerializer,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from common.drf.fields import ReadableHiddenField
|
||||
from common.drf.serializers import DynamicMappingSerializer
|
||||
from common.drf.serializers import MethodSerializer
|
||||
from orgs.utils import get_org_by_id
|
||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||
from users.models import User
|
||||
from tickets import const
|
||||
from tickets.models import Ticket
|
||||
from .meta import meta_field_dynamic_mapping_serializers
|
||||
from .meta import type_serializer_classes_mapping
|
||||
|
||||
__all__ = [
|
||||
'TicketSerializer', 'TicketDisplaySerializer',
|
||||
|
@ -22,7 +22,7 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
|
|||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
||||
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
|
||||
status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status'))
|
||||
meta = DynamicMappingSerializer(mapping_serializers=meta_field_dynamic_mapping_serializers)
|
||||
meta = MethodSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Ticket
|
||||
|
@ -36,14 +36,21 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
|
|||
'body'
|
||||
]
|
||||
|
||||
def get_meta_mapping_path(self, mapping_serializers):
|
||||
view = self.context['view']
|
||||
def get_meta_serializer(self):
|
||||
request = self.context['request']
|
||||
view = self.context['view']
|
||||
query_type = request.query_params.get('type')
|
||||
query_action = request.query_params.get('action')
|
||||
action = query_action if query_action else view.action
|
||||
mapping_path = ['type', query_type, action]
|
||||
return mapping_path
|
||||
view_action = view.action
|
||||
action = query_action if query_action else view_action
|
||||
if query_type:
|
||||
serializer_class = type_serializer_classes_mapping.get(query_type, {}).get(action)
|
||||
else:
|
||||
serializer_class = None
|
||||
if serializer_class is None:
|
||||
serializer_class = serializers.Serializer
|
||||
serializer = serializer_class()
|
||||
return serializer
|
||||
|
||||
|
||||
class TicketDisplaySerializer(TicketSerializer):
|
||||
|
|
Loading…
Reference in New Issue