perf: 修改 model

pull/8873/head
ibuler 2022-09-01 14:46:31 +08:00
parent 4947b0d8fd
commit d7d9fe2718
25 changed files with 216 additions and 383 deletions

View File

@ -77,7 +77,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='platform',
name='su_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='SU method'),
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='SU method'),
),
migrations.AddField(
model_name='platform',

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.14 on 2022-09-01 02:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0107_alter_accountbackupplan_types'),
]
operations = [
migrations.RemoveField(
model_name='platform',
name='create_account_enabled',
),
migrations.RemoveField(
model_name='platform',
name='create_account_method',
),
migrations.RemoveField(
model_name='platform',
name='domain_default',
),
migrations.RemoveField(
model_name='platform',
name='domain_enabled',
),
migrations.RemoveField(
model_name='platform',
name='ping_enabled',
),
migrations.RemoveField(
model_name='platform',
name='ping_method',
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.14 on 2022-09-01 06:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0108_auto_20220901_1034'),
]
operations = [
migrations.RemoveField(
model_name='host',
name='device_info',
),
migrations.AddField(
model_name='asset',
name='info',
field=models.JSONField(blank=True, default=dict, verbose_name='Info'),
),
migrations.AddField(
model_name='database',
name='version',
field=models.CharField(blank=True, max_length=16, verbose_name='Version'),
),
migrations.DeleteModel(
name='DeviceInfo',
),
]

View File

@ -16,7 +16,7 @@ __all__ = ['SystemUser']
logger = logging.getLogger(__name__)
class SystemUser(ProtocolMixin, BaseAccount):
class SystemUser(BaseAccount, ProtocolMixin):
LOGIN_AUTO = 'auto'
LOGIN_MANUAL = 'manual'
LOGIN_MODE_CHOICES = (
@ -44,6 +44,7 @@ class SystemUser(ProtocolMixin, BaseAccount):
# linux su 命令 (switch user)
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from"))
privileged = None
class Meta:
ordering = ['name']

View File

@ -2,14 +2,12 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords
from common.db import fields
from .base import BaseAccount, AbsConnectivity
__all__ = ['Account', 'AccountTemplate']
class Account(BaseAccount, AbsConnectivity):
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
privileged = models.BooleanField(verbose_name=_("Privileged account"), default=False)
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
version = models.IntegerField(default=0, verbose_name=_('Version'))
@ -30,7 +28,6 @@ class Account(BaseAccount, AbsConnectivity):
class AccountTemplate(BaseAccount, AbsConnectivity):
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
privileged = models.BooleanField(verbose_name=_("Privileged account"), default=False)
class Meta:

View File

@ -11,7 +11,6 @@ from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty
from orgs.mixins.models import OrgManager, JMSOrgBaseModel
from ...const import Category
from ..platform import Platform
from ..base import AbsConnectivity
@ -82,7 +81,7 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
info = models.JSONField(verbose_name='Info', default=dict, blank=True)
objects = AssetManager.from_queryset(AssetQuerySet)()
def __str__(self):

View File

@ -6,6 +6,7 @@ from .common import Asset
class Database(Asset):
db_name = models.CharField(max_length=1024, verbose_name=_("Database"), blank=True)
version = models.CharField(max_length=16, verbose_name=_("Version"), blank=True)
def __str__(self):
return '{}({}://{}/{})'.format(self.name, self.type, self.ip, self.db_name)

View File

@ -1,61 +1,8 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from common.mixins.models import CommonModelMixin
from assets.const import Category
from .common import Asset
class Host(Asset):
device_info = models.OneToOneField('DeviceInfo', null=True, on_delete=models.SET_NULL, verbose_name=_("Host"))
def save(self, *args, **kwargs):
self.category = Category.HOST
return super().save(*args, **kwargs)
class DeviceInfo(CommonModelMixin):
# Collect
vendor = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Vendor'))
model = models.CharField(max_length=54, null=True, blank=True, verbose_name=_('Model'))
sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number'))
cpu_model = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU model'))
cpu_count = models.IntegerField(null=True, verbose_name=_('CPU count'))
cpu_cores = models.IntegerField(null=True, verbose_name=_('CPU cores'))
cpu_vcpus = models.IntegerField(null=True, verbose_name=_('CPU vcpus'))
memory = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Memory'))
disk_total = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk total'))
disk_info = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk info'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
os_version = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('OS version'))
os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS arch'))
hostname_raw = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hostname raw'))
number = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Asset number'))
@property
def cpu_info(self):
info = ""
if self.cpu_model:
info += self.cpu_model
if self.cpu_count and self.cpu_cores:
info += "{}*{}".format(self.cpu_count, self.cpu_cores)
return info
@property
def hardware_info(self):
if self.cpu_count:
return '{} Core {} {}'.format(
self.cpu_vcpus or self.cpu_count * self.cpu_cores,
self.memory, self.disk_total
)
else:
return ''
def __str__(self):
return '{} of {}'.format(self.hardware_info, self.host.name)
class Meta:
verbose_name = _("DeviceInfo")

View File

@ -176,7 +176,7 @@ class BaseAccount(OrgModelMixin, AuthMixin):
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'))
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
# token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))

View File

@ -22,8 +22,7 @@ class Domain(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True,
verbose_name=_('Date created'))
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created'))
class Meta:
verbose_name = _("Domain")
@ -64,6 +63,7 @@ class Gateway(BaseAccount):
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("Domain"))
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
token = None
def __str__(self):
return self.name

View File

@ -1,7 +1,7 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from assets.const import Category, AllTypes
from assets.const import AllTypes
from common.db.fields import JsonDictTextField
@ -30,23 +30,14 @@ class Platform(models.Model):
meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta"))
internal = models.BooleanField(default=False, verbose_name=_("Internal"))
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
domain_enabled = models.BooleanField(default=True, verbose_name=_("Domain enabled"))
domain_default = models.ForeignKey(
'assets.Domain', null=True, on_delete=models.SET_NULL,
verbose_name=_("Domain default")
)
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
# Accounts
# 这应该和账号有关
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
su_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("SU method"))
ping_enabled = models.BooleanField(default=False)
ping_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
verify_account_enabled = models.BooleanField(default=False, verbose_name=_("Verify account enabled"))
verify_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Verify account method"))
create_account_enabled = models.BooleanField(default=False, verbose_name=_("Create account enabled"))
create_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Create account method"))
change_password_enabled = models.BooleanField(default=False, verbose_name=_("Change password enabled"))
change_password_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Change password method"))

View File

@ -11,7 +11,8 @@ from .common import BaseAccountSerializer
class AccountSerializer(
AccountTemplateSerializerMixin, AuthSerializerMixin,
AccountTemplateSerializerMixin,
AuthSerializerMixin,
BulkOrgResourceModelSerializer
):
ip = serializers.ReadOnlyField(label=_("IP"))

View File

@ -1,226 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import Asset, Node, Platform, SystemUser
__all__ = [
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
'ProtocolsField', 'PlatformSerializer',
'AssetTaskSerializer', 'AssetsTaskSerializer', 'ProtocolsField',
]
class ProtocolField(serializers.RegexField):
protocols = '|'.join(dict(Asset.Protocol.choices).keys())
default_error_messages = {
'invalid': _('Protocol format should {}/{}').format(protocols, '1-65535')
}
regex = r'^(%s)/(\d{1,5})$' % protocols
def __init__(self, *args, **kwargs):
super().__init__(self.regex, **kwargs)
def validate_duplicate_protocols(values):
errors = []
names = []
for value in values:
if not value or '/' not in value:
continue
name = value.split('/')[0]
if name in names:
errors.append(_("Protocol duplicate: {}").format(name))
names.append(name)
errors.append('')
if any(errors):
raise serializers.ValidationError(errors)
class ProtocolsField(serializers.ListField):
default_validators = [validate_duplicate_protocols]
def __init__(self, *args, **kwargs):
kwargs['child'] = ProtocolField()
kwargs['allow_null'] = True
kwargs['allow_empty'] = True
kwargs['min_length'] = 1
kwargs['max_length'] = 4
super().__init__(*args, **kwargs)
def to_representation(self, value):
if not value:
return []
return value.split(' ')
class AssetSerializer(BulkOrgResourceModelSerializer):
platform = serializers.SlugRelatedField(
slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
)
protocols = ProtocolsField(label=_('Protocols'), required=False, default=['ssh/22'])
domain_display = serializers.ReadOnlyField(source='domain.name', label=_('Domain name'))
nodes_display = serializers.ListField(
child=serializers.CharField(), label=_('Nodes name'), required=False
)
labels_display = serializers.ListField(
child=serializers.CharField(), label=_('Labels name'), required=False, read_only=True
)
"""
资产的数据结构
"""
class Meta:
model = Asset
fields_mini = ['id', 'hostname', 'ip', 'platform', 'protocols']
fields_small = fields_mini + [
'protocol', 'port', 'protocols', 'is_active',
'public_ip', 'number', 'comment',
]
fields_hardware = [
'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
'os', 'os_version', 'os_arch', 'hostname_raw',
'cpu_info', 'hardware_info',
]
fields_fk = [
'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display'
]
fields_m2m = [
'nodes', 'nodes_display', 'labels', 'labels_display',
]
read_only_fields = [
'connectivity', 'date_verified', 'cpu_info', 'hardware_info',
'created_by', 'date_created',
]
fields = fields_small + fields_hardware + fields_fk + fields_m2m + read_only_fields
extra_kwargs = {
'protocol': {'write_only': True},
'port': {'write_only': True},
'hardware_info': {'label': _('Hardware info'), 'read_only': True},
'admin_user_display': {'label': _('Admin user display'), 'read_only': True},
'cpu_info': {'label': _('CPU info')},
}
def get_fields(self):
fields = super().get_fields()
admin_user_field = fields.get('admin_user')
# 因为 mixin 中对 fields 有处理,可能不需要返回 admin_user
if admin_user_field:
admin_user_field.queryset = SystemUser.objects.filter(type=SystemUser.Type.admin)
return fields
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('domain', 'platform', 'admin_user')
queryset = queryset.prefetch_related('nodes', 'labels')
return queryset
def compatible_with_old_protocol(self, validated_data):
protocols_data = validated_data.pop("protocols", [])
# 兼容老的api
name = validated_data.get("protocol")
port = validated_data.get("port")
if not protocols_data and name and port:
protocols_data.insert(0, '/'.join([name, str(port)]))
elif not name and not port and protocols_data:
protocol = protocols_data[0].split('/')
validated_data["protocol"] = protocol[0]
validated_data["port"] = int(protocol[1])
if protocols_data:
validated_data["protocols"] = ' '.join(protocols_data)
def perform_nodes_display_create(self, instance, nodes_display):
if not nodes_display:
return
nodes_to_set = []
for full_value in nodes_display:
node = Node.objects.filter(full_value=full_value).first()
if node:
nodes_to_set.append(node)
else:
node = Node.create_node_by_full_value(full_value)
nodes_to_set.append(node)
instance.nodes.set(nodes_to_set)
def create(self, validated_data):
self.compatible_with_old_protocol(validated_data)
nodes_display = validated_data.pop('nodes_display', '')
instance = super().create(validated_data)
self.perform_nodes_display_create(instance, nodes_display)
return instance
def update(self, instance, validated_data):
nodes_display = validated_data.pop('nodes_display', '')
self.compatible_with_old_protocol(validated_data)
instance = super().update(instance, validated_data)
self.perform_nodes_display_create(instance, nodes_display)
return instance
class MiniAssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = AssetSerializer.Meta.fields_mini
class PlatformSerializer(serializers.ModelSerializer):
meta = serializers.DictField(required=False, allow_null=True, label=_('Meta'))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO 修复 drf SlugField RegexValidator bug之后记得删除
validators = self.fields['name'].validators
if isinstance(validators[-1], RegexValidator):
validators.pop()
class Meta:
model = Platform
fields = [
'id', 'name', 'base', 'charset',
'internal', 'meta', 'comment'
]
extra_kwargs = {
'internal': {'read_only': True},
}
class AssetSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ['id', 'hostname', 'ip', 'port', 'connectivity', 'date_verified']
class AssetsTaskSerializer(serializers.Serializer):
ACTION_CHOICES = (
('refresh', 'refresh'),
('test', 'test'),
)
task = serializers.CharField(read_only=True)
action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True)
assets = serializers.PrimaryKeyRelatedField(
queryset=Asset.objects, required=False, allow_empty=True, many=True
)
class AssetTaskSerializer(AssetsTaskSerializer):
ACTION_CHOICES = tuple(list(AssetsTaskSerializer.ACTION_CHOICES) + [
('push_system_user', 'push_system_user'),
('test_system_user', 'test_system_user')
])
action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True)
asset = serializers.PrimaryKeyRelatedField(
queryset=Asset.objects, required=False, allow_empty=True, many=False
)
system_users = serializers.PrimaryKeyRelatedField(
queryset=SystemUser.objects, required=False, allow_empty=True, many=True
)

View File

@ -1,2 +1,6 @@
from .common import *
from .category import *
from .host import *
from .database import *
from .networking import *
from .cloud import *
from .web import *

View File

@ -1,49 +1,7 @@
from rest_framework import serializers
from assets.models import DeviceInfo, Host, Database, Networking, Cloud, Web
from assets.models import Networking
from .common import AssetSerializer
__all__ = [
'DeviceSerializer', 'HostSerializer', 'DatabaseSerializer',
'NetworkingSerializer', 'CloudSerializer', 'WebSerializer',
]
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = DeviceInfo
fields = [
'id', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
'os', 'os_version', 'os_arch', 'hostname_raw', 'number',
'cpu_info', 'hardware_info', 'date_updated'
]
class HostSerializer(AssetSerializer):
device_info = DeviceSerializer(read_only=True, allow_null=True)
class Meta(AssetSerializer.Meta):
model = Host
fields = AssetSerializer.Meta.fields + ['device_info']
class DatabaseSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Database
fields = AssetSerializer.Meta.fields + ['db_name']
class WebSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Web
fields = AssetSerializer.Meta.fields + ['url']
class CloudSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Cloud
fields = AssetSerializer.Meta.fields + ['cluster']
__all__ = ['NetworkingSerializer']
class NetworkingSerializer(AssetSerializer):

View File

@ -0,0 +1,11 @@
from assets.models import Cloud
from .common import AssetSerializer
__all__ = ['CloudSerializer']
class CloudSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Cloud
fields = AssetSerializer.Meta.fields + ['cluster']

View File

@ -6,7 +6,7 @@ from django.db.transaction import atomic
from django.db.models import F
from common.drf.serializers import JMSWritableNestedModelSerializer
from common.drf.fields import ChoiceDisplayField
from common.drf.fields import LabeledChoiceField, ObjectedRelatedField
from ..account import AccountSerializer
from ...models import Asset, Node, Platform, Protocol, Label, Domain
from ...const import Category, AllTypes
@ -42,33 +42,15 @@ class AssetPlatformSerializer(serializers.ModelSerializer):
}
class AssetDomainSerializer(serializers.ModelSerializer):
class Meta:
model = Domain
fields = ['id', 'name']
extra_kwargs = {
'name': {'required': False}
}
class AssetNodesSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = ['id', 'value']
extra_kwargs = {
'value': {'required': False}
}
class AssetSerializer(JMSWritableNestedModelSerializer):
category = ChoiceDisplayField(choices=Category.choices, read_only=True, label=_('Category'))
type = ChoiceDisplayField(choices=AllTypes.choices, read_only=True, label=_('Type'))
domain = AssetDomainSerializer(required=False)
platform = AssetPlatformSerializer(required=False)
labels = AssetLabelSerializer(many=True, required=False)
nodes = AssetNodesSerializer(many=True, required=False)
accounts = AccountSerializer(many=True, required=False)
protocols = AssetProtocolsSerializer(many=True, required=False)
category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category'))
type = LabeledChoiceField(choices=AllTypes.choices, read_only=True, label=_('Type'))
domain = ObjectedRelatedField(required=False, queryset=Domain.objects, label=_('Domain'))
platform = ObjectedRelatedField(required=False, queryset=Platform.objects, label=_('Platform'))
nodes = ObjectedRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
labels = AssetLabelSerializer(many=True, required=False, label=_('Labels'))
accounts = AccountSerializer(many=True, required=False, label=_('Accounts'))
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
"""
资产的数据结构

View File

@ -0,0 +1,11 @@
from assets.models import Database
from .common import AssetSerializer
__all__ = ['DatabaseSerializer']
class DatabaseSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Database
fields = AssetSerializer.Meta.fields + ['db_name']

View File

@ -0,0 +1,37 @@
from rest_framework import serializers
from django.utils.translation import gettext_lazy as _
from assets.models import Host
from .common import AssetSerializer
__all__ = ['HostInfoSerializer', 'HostSerializer']
class HostInfoSerializer(serializers.Serializer):
vendor = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Vendor'))
model = serializers.CharField(max_length=54, required=False, allow_blank=True, label=_('Model'))
sn = serializers.CharField(max_length=128, required=False, allow_blank=True, label=_('Serial number'))
cpu_model = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU model'))
cpu_count = serializers.IntegerField(required=False, label=_('CPU count'))
cpu_cores = serializers.IntegerField(required=False, label=_('CPU cores'))
cpu_vcpus = serializers.IntegerField(required=False, label=_('CPU vcpus'))
memory = serializers.CharField(max_length=64, allow_blank=True, required=False, label=_('Memory'))
disk_total = serializers.CharField(max_length=1024, allow_blank=True, required=False, label=_('Disk total'))
disk_info = serializers.CharField(max_length=1024, allow_blank=True, required=False, label=_('Disk info'))
os = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('OS'))
os_version = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS version'))
os_arch = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS arch'))
hostname_raw = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Hostname raw'))
number = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Asset number'))
class HostSerializer(AssetSerializer):
info = HostInfoSerializer(allow_null=True)
class Meta(AssetSerializer.Meta):
model = Host
fields = AssetSerializer.Meta.fields + ['info']

View File

@ -0,0 +1,10 @@
from assets.models import Networking
from .common import AssetSerializer
__all__ = ['NetworkingSerializer']
class NetworkingSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Networking

View File

@ -0,0 +1,11 @@
from assets.models import Web
from .common import AssetSerializer
__all__ = ['WebSerializer']
class WebSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta):
model = Web
fields = AssetSerializer.Meta.fields + ['url']

View File

@ -1,7 +1,7 @@
from rest_framework import serializers
from django.utils.translation import gettext_lazy as _
from common.drf.fields import ChoiceDisplayField
from common.drf.fields import LabeledChoiceField
from common.drf.serializers import JMSWritableNestedModelSerializer
from ..models import Platform, PlatformProtocol
from ..const import Category, AllTypes
@ -30,11 +30,11 @@ class PlatformProtocolsSerializer(serializers.ModelSerializer):
class PlatformSerializer(JMSWritableNestedModelSerializer):
type = ChoiceDisplayField(choices=AllTypes.choices, label=_("Type"))
category = ChoiceDisplayField(choices=Category.choices, label=_("Category"))
type = LabeledChoiceField(choices=AllTypes.choices, label=_("Type"))
category = LabeledChoiceField(choices=Category.choices, label=_("Category"))
protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
su_method = ChoiceDisplayField(
su_method = LabeledChoiceField(
choices=[('sudo', 'sudo su -'), ('su', 'su - ')],
label='切换方式', required=False, default='sudo'
)
@ -54,6 +54,8 @@ class PlatformSerializer(JMSWritableNestedModelSerializer):
]
extra_kwargs = {
'su_enabled': {'label': '启用切换账号'},
'domain_enabled': {'label': "启用网域"},
'domain_default': {'label': "默认网域"},
'verify_account_enabled': {'label': '启用校验账号'},
'verify_account_method': {'label': '校验账号方式'},
'create_account_enabled': {'label': '启用创建账号'},

View File

@ -4,11 +4,13 @@ import six
from rest_framework.fields import ChoiceField
from rest_framework import serializers
from django.core.exceptions import ObjectDoesNotExist
from common.utils import decrypt_password
__all__ = [
'ReadableHiddenField', 'EncryptedField', 'ChoiceDisplayField'
'ReadableHiddenField', 'EncryptedField', 'LabeledChoiceField',
'ObjectedRelatedField',
]
@ -40,9 +42,9 @@ class EncryptedField(serializers.CharField):
return decrypt_password(value)
class ChoiceDisplayField(ChoiceField):
class LabeledChoiceField(ChoiceField):
def __init__(self, *args, **kwargs):
super(ChoiceDisplayField, self).__init__(*args, **kwargs)
super(LabeledChoiceField, self).__init__(*args, **kwargs)
self.choice_mapper = {
six.text_type(key): value for key, value in self.choices.items()
}
@ -58,4 +60,31 @@ class ChoiceDisplayField(ChoiceField):
def to_internal_value(self, data):
if isinstance(data, dict):
return data.get('value')
return super(ChoiceDisplayField, self).to_internal_value(data)
return super(LabeledChoiceField, self).to_internal_value(data)
class ObjectedRelatedField(serializers.RelatedField):
def __init__(self, **kwargs):
self.attrs = kwargs.pop('attrs', None) or ('id', 'name')
super().__init__(**kwargs)
def to_representation(self, value):
data = {}
for attr in self.attrs:
data[attr] = getattr(value, attr)
return data
def to_internal_value(self, data):
if isinstance(data, dict):
pk = data.get(self.attrs[0])
else:
pk = data
queryset = self.get_queryset()
try:
if isinstance(data, bool):
raise TypeError
return queryset.get(pk=pk)
except ObjectDoesNotExist:
self.fail('does_not_exist', pk_value=pk)
except (TypeError, ValueError):
self.fail('incorrect_type', data_type=type(pk).__name__)

View File

@ -85,13 +85,13 @@ class SimpleMetadataWithFilters(SimpleMetadata):
field_info['choices'] = [
{
'value': choice_value,
'display_name': force_text(choice_name, strings_only=True)
'label': force_text(choice_name, strings_only=True)
}
for choice_value, choice_name in dict(field.choices).items()
]
if field.__class__.__name__ == 'ChoiceDisplayField':
field_info['type'] = 'display_choice'
if field.__class__.__name__ == 'LabeledChoiceField':
field_info['type'] = 'labeled_choice'
return field_info
def get_filters_fields(self, request, view):