xadmin如何自定义字段在页面上的显示效果. 比如使用自己特定的js控件

Q: 如何自定义字段在页面上的显示效果. 比如使用自己特定的js控件

A:

  1. 可以自定义widgets
  2. 自定义form将widgets设置到form的对应的field上
  3. 在OptionClass里设置form
  4. 将OptionClass设置到xadmin上

举例:

在页面上显示tags字段时候想使用select2.js这个模板来实现这样的效果. 也就是多选的select框, 选中的放在上面作为标签, 可以叉掉.

models.py

首先models.py里面tags字段使用了postgres才有的ArrayField来存储多个选项

from django.contrib.postgres.fields import ArrayField
from django.db import models

class Case(models.Model):
    tags = ArrayField(models.CharField(max_length=200), null=True, blank=True)

widgets.py

要将前端接收到的tags[]字段修改成tag才能保存

import os

import pandas as pd
from django import forms
from django.utils.safestring import mark_safe

pwd = os.path.abspath(os.path.dirname(__file__))

# 加载tag标签的所有值
df = pd.read_csv(os.path.join(pwd, "hunting_rules_db.tags_list.csv"))
tags_list = list(df["name"].values)

# 要注入到页面上的js
JS = '''
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.min.js"></script>
<script>
    $(function () {
        $('.js-example-basic-multiple').select2();
    });
</script>
'''


class Select2Widget(forms.SelectMultiple):
    # 要处理多选的字段
    array_field_list = ['tags']

    def value_from_datadict(self, data, files, name):
        """
        处理前端是tags[]后端tag
        :param data:
        :param files:
        :param name:
        :return:
        """
        if name in self.array_field_list:
            name += '[]'
        try:
            getter = data.getlist
        except AttributeError:
            getter = data.get
        return getter(name)

    def render(self, name, value, attrs=None, renderer=None):
        '''
        关键方法
        :param name:
        :param value:
        :param attrs:
        :return:
        '''
        output = [JS]
        output.append('<select class="js-example-basic-multiple" name="tags[]" multiple="multiple">')
        tags_list = self.get_tags_list()
        if value is None:
            value = []
        for tag in tags_list:
            if tag in value:
                selected = ' selected="selected"'
            else:
                selected = ''
            output.append(f'<option value="{tag}" {selected}>{tag}</option>')
        output.append('</select>')

        return mark_safe('\n'.join(output))

    def get_tags_list(self):
        return tags_list

adminx.py

from django.contrib.postgres.forms import SimpleArrayField
from django.forms import ModelForm, CharField

import xadmin
from xadmin import views
from .models import Case, Ioc, FileAttachment
from .widgets import Select2Widget

#自定义的widget注册到form中
class CaseForm(ModelForm):
    tags = SimpleArrayField(CharField(), widget=Select2Widget, required=False)

# 自定义的form注册到OptionClass中
class BaseAdmin:
    list_display = ["name", "happen_datetime", "TLP", "tags", "len_ioc", "len_file", "update_user", "update_time"]
    form = CaseForm