mirror of https://github.com/jumpserver/jumpserver
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
6.4 KiB
179 lines
6.4 KiB
# -*- coding: utf-8 -*-
|
|
#
|
|
from __future__ import unicode_literals
|
|
|
|
import datetime
|
|
from collections import OrderedDict
|
|
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import Http404
|
|
from django.utils.encoding import force_str
|
|
from django.utils.translation import gettext_lazy as _
|
|
from rest_framework import exceptions, serializers
|
|
from rest_framework.fields import empty
|
|
from rest_framework.metadata import SimpleMetadata
|
|
from rest_framework.request import clone_request
|
|
|
|
from common.serializers.fields import TreeChoicesField
|
|
|
|
|
|
class SimpleMetadataWithFilters(SimpleMetadata):
|
|
"""Override SimpleMetadata, adding info about filters"""
|
|
|
|
methods = {"PUT", "POST", "GET", "PATCH"}
|
|
attrs = [
|
|
"read_only", "label", "help_text",
|
|
"min_length", "max_length", "min_value",
|
|
"max_value", "write_only",
|
|
]
|
|
|
|
def determine_actions(self, request, view):
|
|
"""
|
|
For generic class based views we return information about
|
|
the fields that are accepted for 'PUT' and 'POST' methods.
|
|
"""
|
|
actions = {}
|
|
view.raw_action = getattr(view, "action", None)
|
|
for method in self.methods & set(view.allowed_methods):
|
|
if hasattr(view, "action_map"):
|
|
view.action = view.action_map.get(method.lower(), view.action)
|
|
|
|
view.request = clone_request(request, method)
|
|
try:
|
|
# Test global permissions
|
|
if hasattr(view, "check_permissions"):
|
|
view.check_permissions(view.request)
|
|
# Test object permissions
|
|
if method == "PUT" and hasattr(view, "get_object"):
|
|
view.get_object()
|
|
except (exceptions.APIException, PermissionDenied, Http404):
|
|
pass
|
|
else:
|
|
# If user has appropriate permissions for the view, include
|
|
# appropriate metadata about the fields that should be supplied.
|
|
serializer = view.get_serializer()
|
|
actions[method] = self.get_serializer_info(serializer)
|
|
finally:
|
|
view.request = request
|
|
return actions
|
|
|
|
def get_field_type(self, field):
|
|
"""
|
|
Given a field, return a string representing the type of the field.
|
|
"""
|
|
tp = self.label_lookup[field]
|
|
|
|
class_name = field.__class__.__name__
|
|
if class_name == "LabeledChoiceField":
|
|
tp = "labeled_choice"
|
|
elif class_name == "JSONField":
|
|
tp = 'json'
|
|
elif class_name == "ObjectRelatedField":
|
|
tp = "object_related_field"
|
|
elif class_name == "ManyRelatedField":
|
|
child_relation_class_name = field.child_relation.__class__.__name__
|
|
if child_relation_class_name == "ObjectRelatedField":
|
|
tp = "m2m_related_field"
|
|
return tp
|
|
|
|
@staticmethod
|
|
def set_choices_field(field, field_info):
|
|
field_info["choices"] = [
|
|
{
|
|
"value": choice_value,
|
|
"label": force_str(choice_label, strings_only=True),
|
|
}
|
|
for choice_value, choice_label in dict(field.choices).items()
|
|
]
|
|
|
|
@staticmethod
|
|
def set_tree_field(field, field_info):
|
|
field_info["tree"] = field.tree
|
|
field_info["type"] = "tree"
|
|
|
|
def get_field_info(self, field):
|
|
"""
|
|
Given an instance of a serializer field, return a dictionary
|
|
of metadata about it.
|
|
"""
|
|
field_info = OrderedDict()
|
|
field_info["type"] = self.get_field_type(field)
|
|
field_info["required"] = getattr(field, "required", False)
|
|
|
|
# Default value
|
|
default = getattr(field, "default", None)
|
|
if default is not None and default != empty:
|
|
if isinstance(default, (str, int, bool, float, datetime.datetime, list)):
|
|
field_info["default"] = default
|
|
|
|
for attr in self.attrs:
|
|
value = getattr(field, attr, None)
|
|
if value is not None and value != "":
|
|
field_info[attr] = force_str(value, strings_only=True)
|
|
|
|
if getattr(field, "child", None):
|
|
field_info["child"] = self.get_field_info(field.child)
|
|
elif getattr(field, "fields", None):
|
|
field_info["children"] = self.get_serializer_info(field)
|
|
|
|
if isinstance(field, TreeChoicesField):
|
|
self.set_tree_field(field, field_info)
|
|
elif isinstance(field, serializers.ChoiceField):
|
|
self.set_choices_field(field, field_info)
|
|
|
|
if field.field_name == 'id':
|
|
field_info['label'] = 'ID'
|
|
if field.field_name == 'org_id':
|
|
field_info['label'] = _('Organization ID')
|
|
|
|
return field_info
|
|
|
|
@staticmethod
|
|
def get_filters_fields(request, view):
|
|
fields = []
|
|
if hasattr(view, "get_filter_fields"):
|
|
fields = view.get_filter_fields(request)
|
|
elif hasattr(view, "filter_fields"):
|
|
fields = view.filter_fields
|
|
elif hasattr(view, "filterset_fields"):
|
|
fields = view.filterset_fields
|
|
elif hasattr(view, "get_filterset_fields"):
|
|
fields = view.get_filterset_fields(request)
|
|
elif hasattr(view, "filterset_class"):
|
|
fields = list(view.filterset_class.Meta.fields) + list(
|
|
view.filterset_class.declared_filters.keys()
|
|
)
|
|
|
|
if hasattr(view, "custom_filter_fields"):
|
|
# 不能写 fields += view.custom_filter_fields
|
|
# 会改变 view 的 filter_fields
|
|
fields = list(fields) + list(view.custom_filter_fields)
|
|
|
|
if isinstance(fields, dict):
|
|
fields = list(fields.keys())
|
|
return fields
|
|
|
|
@staticmethod
|
|
def get_ordering_fields(request, view):
|
|
fields = []
|
|
if hasattr(view, "get_ordering_fields"):
|
|
fields = view.get_ordering_fields(request)
|
|
elif hasattr(view, "ordering_fields"):
|
|
fields = view.ordering_fields
|
|
return fields
|
|
|
|
def determine_metadata(self, request, view):
|
|
metadata = super(SimpleMetadataWithFilters, self).determine_metadata(
|
|
request, view
|
|
)
|
|
filterset_fields = self.get_filters_fields(request, view)
|
|
order_fields = self.get_ordering_fields(request, view)
|
|
|
|
meta_get = metadata.get("actions", {}).get("GET", {})
|
|
for k, v in meta_get.items():
|
|
if k in filterset_fields:
|
|
v["filter"] = True
|
|
if k in order_fields:
|
|
v["order"] = True
|
|
return metadata
|