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 rest_framework import serializers
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from common.drf.serializers import DynamicMappingSerializer
|
from common.drf.serializers import MethodSerializer
|
||||||
from .attrs import attrs_field_dynamic_mapping_serializers
|
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
|
||||||
|
|
||||||
from .. import models
|
from .. import models
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ApplicationSerializer',
|
'ApplicationSerializer', 'ApplicationSerializerMixin',
|
||||||
'IncludeDynamicMappingSerializerFieldApplicationSerializerMixin',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class IncludeDynamicMappingSerializerFieldApplicationSerializerMixin(serializers.Serializer):
|
class ApplicationSerializerMixin(serializers.Serializer):
|
||||||
attrs = DynamicMappingSerializer(mapping_serializers=attrs_field_dynamic_mapping_serializers)
|
attrs = MethodSerializer()
|
||||||
|
|
||||||
def get_attrs_mapping_path(self, mapping_serializers):
|
def get_attrs_serializer(self):
|
||||||
request = self.context['request']
|
request = self.context['request']
|
||||||
query_type = request.query_params.get('type')
|
query_type = request.query_params.get('type')
|
||||||
query_category = request.query_params.get('category')
|
query_category = request.query_params.get('category')
|
||||||
if query_type:
|
if query_type:
|
||||||
mapping_path = ['type', query_type]
|
serializer_class = type_serializer_classes_mapping.get(query_type)
|
||||||
elif query_category:
|
elif query_category:
|
||||||
mapping_path = ['category', query_category]
|
serializer_class = category_serializer_classes_mapping.get(query_category)
|
||||||
else:
|
else:
|
||||||
mapping_path = ['default']
|
serializer_class = None
|
||||||
return mapping_path
|
if serializer_class is None:
|
||||||
|
serializer_class = serializers.Serializer
|
||||||
|
serializer = serializer_class()
|
||||||
|
return serializer
|
||||||
|
|
||||||
|
|
||||||
class ApplicationSerializer(IncludeDynamicMappingSerializerFieldApplicationSerializerMixin,
|
class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
BulkOrgResourceModelSerializer):
|
|
||||||
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category'))
|
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category'))
|
||||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
||||||
|
|
||||||
|
|
|
@ -4,62 +4,39 @@ from . import application_category, application_type
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'attrs_field_dynamic_mapping_serializers',
|
'category_serializer_classes_mapping',
|
||||||
|
'type_serializer_classes_mapping',
|
||||||
'get_serializer_class_by_application_type',
|
'get_serializer_class_by_application_type',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# application category
|
# define `attrs` field `category serializers mapping`
|
||||||
# --------------------
|
# ---------------------------------------------------
|
||||||
|
|
||||||
category_db = const.ApplicationCategoryChoices.db.value
|
category_serializer_classes_mapping = {
|
||||||
category_remote_app = const.ApplicationCategoryChoices.remote_app.value
|
const.ApplicationCategoryChoices.db.value: application_category.DBSerializer,
|
||||||
category_cloud = const.ApplicationCategoryChoices.cloud.value
|
const.ApplicationCategoryChoices.remote_app.value: application_category.RemoteAppSerializer,
|
||||||
|
const.ApplicationCategoryChoices.cloud.value: application_category.CloudSerializer,
|
||||||
|
|
||||||
# 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# define `attrs` field `type serializers mapping`
|
||||||
|
# -----------------------------------------------
|
||||||
|
|
||||||
|
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):
|
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 import serializers
|
||||||
from rest_framework.serializers import Serializer
|
from rest_framework.serializers import Serializer
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
|
@ -8,97 +7,51 @@ from common.mixins import BulkListSerializerMixin
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from rest_framework.utils.serializer_helpers import BindingDict
|
from rest_framework.utils.serializer_helpers import BindingDict
|
||||||
from common.mixins.serializers import BulkSerializerMixin
|
from common.mixins.serializers import BulkSerializerMixin
|
||||||
from common.utils import QuickLookupDict
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'DynamicMappingSerializer',
|
'MethodSerializer',
|
||||||
'EmptySerializer', 'BulkModelSerializer', 'AdaptedBulkListSerializer', 'CeleryTaskSerializer'
|
'EmptySerializer', 'BulkModelSerializer', 'AdaptedBulkListSerializer', 'CeleryTaskSerializer'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# DynamicMappingSerializer
|
# MethodSerializer
|
||||||
# ------------------------
|
# ----------------
|
||||||
|
|
||||||
|
|
||||||
class DynamicMappingSerializer(serializers.Serializer):
|
class MethodSerializer(serializers.Serializer):
|
||||||
data_type_error_messages = 'Expect get instance of type `{}`, but got instance type of `{}`'
|
|
||||||
|
|
||||||
def __init__(self, mapping_serializers=None, get_mapping_serializers_method_name=None,
|
def __init__(self, method_name=None, **kwargs):
|
||||||
get_mapping_path_method_name=None, default_serializer=None, **kwargs):
|
self.method_name = method_name
|
||||||
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
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def bind(self, field_name, parent):
|
def bind(self, field_name, parent):
|
||||||
# The get mapping serializers method name defaults to `get_{field_name}_mapping_serializers`
|
if self.method_name is None:
|
||||||
if self.get_mapping_serializers_method_name is None:
|
method_name = 'get_{field_name}_serializer'.format(field_name=field_name)
|
||||||
method_name = 'get_{field_name}_mapping_serializers'.format(field_name=field_name)
|
self.method_name = method_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
|
|
||||||
|
|
||||||
super().bind(field_name, parent)
|
super().bind(field_name, parent)
|
||||||
|
|
||||||
def get_mapping_serializers(self):
|
@cached_property
|
||||||
if self.mapping_serializers is not None:
|
def serializer(self) -> serializers.Serializer:
|
||||||
return self.mapping_serializers
|
method = getattr(self.parent, self.method_name)
|
||||||
method = getattr(self.parent, self.get_mapping_serializers_method_name)
|
|
||||||
return method()
|
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):
|
def get_fields(self):
|
||||||
fields = self.mapped_serializer.get_fields()
|
return self.serializer.get_fields()
|
||||||
return fields
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def fields(self):
|
def fields(self):
|
||||||
"""
|
"""
|
||||||
重写此方法因为在 BindingDict 中要设置每一个 field 的 parent 为 `mapped_serializer`,
|
重写此方法因为在 BindingDict 中要设置每一个 field 的 parent 为 `serializer`,
|
||||||
这样在调用 field.parent 时, 才会达到预期的结果,
|
这样在调用 field.parent 时, 才会达到预期的结果,
|
||||||
比如: serializers.SerializerMethodField
|
比如: serializers.SerializerMethodField
|
||||||
"""
|
"""
|
||||||
fields = BindingDict(self.mapped_serializer)
|
fields = BindingDict(self.serializer)
|
||||||
for key, value in self.get_fields().items():
|
for key, value in self.get_fields().items():
|
||||||
fields[key] = value
|
fields[key] = value
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Other Serializer
|
# Other Serializer
|
||||||
# ----------------
|
# ----------------
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from assets.models import SystemUser
|
from assets.models import SystemUser
|
||||||
from applications.models import Application
|
from applications.models import Application
|
||||||
from applications.serializers import IncludeDynamicMappingSerializerFieldApplicationSerializerMixin
|
from applications.serializers import ApplicationSerializerMixin
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ApplicationGrantedSerializer', 'ApplicationSystemUserSerializer'
|
'ApplicationGrantedSerializer', 'ApplicationSystemUserSerializer'
|
||||||
|
@ -26,8 +26,7 @@ class ApplicationSystemUserSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = fields
|
read_only_fields = fields
|
||||||
|
|
||||||
|
|
||||||
class ApplicationGrantedSerializer(IncludeDynamicMappingSerializerFieldApplicationSerializerMixin,
|
class ApplicationGrantedSerializer(ApplicationSerializerMixin, serializers.ModelSerializer):
|
||||||
serializers.ModelSerializer):
|
|
||||||
"""
|
"""
|
||||||
被授权应用的数据结构
|
被授权应用的数据结构
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,46 +2,29 @@ from tickets import const
|
||||||
from .ticket_type import apply_asset, apply_application, login_confirm
|
from .ticket_type import apply_asset, apply_application, login_confirm
|
||||||
|
|
||||||
__all__ = [
|
__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
|
# ticket action
|
||||||
# -------------
|
# -------------
|
||||||
|
|
||||||
|
|
||||||
actions = const.TicketActionChoices.values
|
|
||||||
action_open = const.TicketActionChoices.open.value
|
action_open = const.TicketActionChoices.open.value
|
||||||
action_approve = const.TicketActionChoices.approve.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
|
# defines `meta` field dynamic mapping serializers
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
|
type_serializer_classes_mapping = {
|
||||||
meta_field_dynamic_mapping_serializers = {
|
const.TicketTypeChoices.apply_asset.value: {
|
||||||
'type': {
|
|
||||||
type_apply_asset: {
|
|
||||||
action_open: apply_asset.ApplySerializer,
|
action_open: apply_asset.ApplySerializer,
|
||||||
action_approve: apply_asset.ApproveSerializer,
|
action_approve: apply_asset.ApproveSerializer,
|
||||||
},
|
},
|
||||||
type_apply_application: {
|
const.TicketTypeChoices.apply_application.value: {
|
||||||
action_open: apply_application.ApplySerializer,
|
action_open: apply_application.ApplySerializer,
|
||||||
action_approve: apply_application.ApproveSerializer,
|
action_approve: apply_application.ApproveSerializer,
|
||||||
},
|
},
|
||||||
type_login_confirm: {
|
const.TicketTypeChoices.login_confirm.value: {
|
||||||
action_open: login_confirm.ApplySerializer,
|
action_open: login_confirm.ApplySerializer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from common.drf.fields import ReadableHiddenField
|
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.utils import get_org_by_id
|
||||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from tickets import const
|
from tickets import const
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
from .meta import meta_field_dynamic_mapping_serializers
|
from .meta import type_serializer_classes_mapping
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'TicketSerializer', 'TicketDisplaySerializer',
|
'TicketSerializer', 'TicketDisplaySerializer',
|
||||||
|
@ -22,7 +22,7 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
|
||||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
||||||
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
|
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
|
||||||
status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status'))
|
status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status'))
|
||||||
meta = DynamicMappingSerializer(mapping_serializers=meta_field_dynamic_mapping_serializers)
|
meta = MethodSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ticket
|
model = Ticket
|
||||||
|
@ -36,14 +36,21 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
|
||||||
'body'
|
'body'
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_meta_mapping_path(self, mapping_serializers):
|
def get_meta_serializer(self):
|
||||||
view = self.context['view']
|
|
||||||
request = self.context['request']
|
request = self.context['request']
|
||||||
|
view = self.context['view']
|
||||||
query_type = request.query_params.get('type')
|
query_type = request.query_params.get('type')
|
||||||
query_action = request.query_params.get('action')
|
query_action = request.query_params.get('action')
|
||||||
action = query_action if query_action else view.action
|
view_action = view.action
|
||||||
mapping_path = ['type', query_type, action]
|
action = query_action if query_action else view_action
|
||||||
return mapping_path
|
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):
|
class TicketDisplaySerializer(TicketSerializer):
|
||||||
|
|
Loading…
Reference in New Issue