Package: hadmin
HAdmin后端对DRF封装我想实现一个这样的理想国：比xadmin漂亮；比xadmin强大；前后端分离；基于Django和DRF；智能化配置。以上几点，是我们对HAdmin的设想，设想这样的后端能够相对自动化输出接口，让前端能够根据配置文件认识输出的标准数据结构，从而进行渲染。于是，初步版本的HAdmin上线了。特别说明这个模块只是帮助构建给前端的配置信息，让前端方便进行对应渲染，并不是给前端输送代码！这个请大家一定要理解清楚，这里仅仅是帮助规范了配置信息结构而已。快速开始安装pipinstallhadmin使用方式fromrest_frameworkimportviewsetsfromhadminimportmixinsclassCustomConfigViewSet(mixins.PageConfigMixin,viewsets.GenericViewSet):list_class=CustomListSerializerfilter_class=CustomFiltercreate_class=CustomCreateSerializerread_class=CustomReadSerializerextra={}其实，就是用HAdmin下的mixins替代rest_framework下的mixins并且，增加了一个PageConfigMixin方法，这个方法包含了四种配置文件的输出方式，分别是：list_class 列表的配置信息filter_class 筛选器的配置信息create_class 创建数据表单的配置信息read_class 读取最终数据的配置信息extra 是额外扩展的数据，可以是字典或者列表等任意可以转换成json的值，会原样递送到前端，用于前端渲染特殊情况下使用，非必填，根据需要使用每个对应的都是序列化器的的类，序列化器建议每个方法用不同类，这样可以自由定义。每个序列化器类的元数据可以增加一个custom字典，以便进行解析。输出到前端的数据样例：{"filter":{"base":[{"label":"手机号","method":"input","default":"","url":"","limit":1,"span":6,"key":"mobile"},{"label":"姓名","method":"input","default":"","url":"","limit":1,"span":6,"key":"name"},{"label":"姓名","method":"input","default":"","url":"","limit":1,"span":6,"key":"desc"},{"label":"新房客源","method":"select","default":"","url":"","limit":1,"span":6,"key":"new","options":[{"id":"-1000","title":"不限"},{"id":"true","title":"是"},{"id":"false","title":"否"},{"id":"isnull","title":"未填写"}]},{"label":"二手房客源","method":"select","default":"","url":"","limit":1,"span":6,"key":"old","options":[{"id":"-1000","title":"不限"},{"id":"true","title":"是"},{"id":"false","title":"否"},{"id":"isnull","title":"未填写"}]},{"label":"租房客源","method":"select","default":"","url":"","limit":1,"span":6,"key":"rent","options":[{"id":"-1000","title":"不限"},{"id":"true","title":"是"},{"id":"false","title":"否"},{"id":"isnull","title":"未填写"}]}],"adv":[],"height":100,"fields":{"mobile":"","name":"","desc":"","new":"","old":"","rent":""}},"create":{"fields":{"master":null,"region":null,"admins":[],"mobile":"","name":"","new":false,"old":false,"rent":false,"desc":"","custom":[]},"inlines":{"custom":{"label":"跟踪记录","create":{"fields":{"custom":null,"date":null,"body":"","result":"","image":null,"file":null},"detail":[{"label":"客源","help_text":null,"field_name":"custom","required":true,"type":"PrimaryKeyRelatedField","rules":[{"required":true,"message":"该字段是必填项。"}],"choices":[{"value":13,"label":"18888888885"},{"value":12,"label":"18888888884"},{"value":11,"label":"18888888883"},{"value":10,"label":"18888888882"},{"value":8,"label":"13999999990"},{"value":7,"label":"13999999994"},{"value":6,"label":"13999999995"},{"value":5,"label":"13999999996"},{"value":4,"label":"13999999997"},{"value":3,"label":"13999999998"},{"value":2,"label":"13999999999"},{"value":1,"label":"13888888888"}],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"ForeignKey"},{"label":"时间","help_text":null,"field_name":"date","required":true,"type":"DateField","rules":[{"required":true,"message":"该字段是必填项。"}],"choices":[],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"DateField"},{"label":"内容","help_text":null,"field_name":"body","required":true,"type":"CharField","rules":[{"required":true,"message":"该字段是必填项。"}],"choices":[],"choices_apis":[],"method":"","max_length":10000,"precision":null,"field":"TextField"},{"label":"结果","help_text":null,"field_name":"result","required":false,"type":"CharField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":10000,"precision":null,"field":"TextField"},{"label":"图片","help_text":null,"field_name":"image","required":false,"type":"ImageField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":100,"precision":null,"field":"ImageField"},{"label":"附件","help_text":null,"field_name":"file","required":false,"type":"FileField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":100,"precision":null,"field":"FileField"}],"inlines":{},"tabs":[]},"limit":10,"api":"/admin/user/track/data/"}},"detail":[{"label":"主理人","help_text":null,"field_name":"master","required":false,"type":"PrimaryKeyRelatedField","rules":[],"choices":[{"value":1,"label":"hofeng"},{"value":13,"label":"18961330033"},{"value":83,"label":"shanghai001"},{"value":84,"label":"shanghai002"},{"value":85,"label":"shanghai003"},{"value":86,"label":"suqian001"}],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"ForeignKey"},{"label":"地区","help_text":null,"field_name":"region","required":true,"type":"PrimaryKeyRelatedField","rules":[{"required":true,"message":"该字段是必填项。"}],"choices":[{"value":1,"label":"上海【310000】"},{"value":21,"label":"宿迁【321300】"},{"value":34,"label":"盐城【320900】"}],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"ForeignKey"},{"label":"协作人","help_text":null,"field_name":"admins","required":false,"type":"ManyRelatedField","rules":[],"choices":[{"value":1,"label":"hofeng"},{"value":13,"label":"18961330033"},{"value":83,"label":"shanghai001"},{"value":84,"label":"shanghai002"},{"value":85,"label":"shanghai003"},{"value":86,"label":"suqian001"}],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"ManyToManyField"},{"label":"手机号","help_text":"客源手机号","field_name":"mobile","required":true,"type":"CharField","rules":[{"required":true,"message":"该字段是必填项。"}],"choices":[],"choices_apis":[],"method":"","max_length":11,"precision":null,"field":"CharField"},{"label":"姓名","help_text":null,"field_name":"name","required":false,"type":"CharField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":32,"precision":null,"field":"CharField"},{"label":"新房客源","help_text":null,"field_name":"new","required":false,"type":"BooleanField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"BooleanField"},{"label":"二手房客源","help_text":null,"field_name":"old","required":false,"type":"BooleanField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"BooleanField"},{"label":"租房客源","help_text":null,"field_name":"rent","required":false,"type":"BooleanField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":0,"precision":null,"field":"BooleanField"},{"label":"简要说明","help_text":null,"field_name":"desc","required":false,"type":"CharField","rules":[],"choices":[],"choices_apis":[],"method":"","max_length":2000,"precision":null,"field":"TextField"}],"tabs":[]},"list":[{"label":"ID","field_name":"id","type":"IntegerField","field":"AutoField","width":"80"},{"label":"手机号","field_name":"mobile","type":"CharField","field":"CharField"},{"label":"姓名","field_name":"name","type":"CharField","field":"CharField"},{"label":"主理人","field_name":"master","type":"StringRelatedField","field":"ForeignKey"},{"label":"新房客源","field_name":"new","type":"BooleanField","field":"BooleanField"},{"label":"二手房客源","field_name":"old","type":"BooleanField","field":"BooleanField"},{"label":"租房客源","field_name":"rent","type":"BooleanField","field":"BooleanField"},{"label":"添加时间","field_name":"add_time","type":"DateTimeField","field":"DateTimeField"}],"read":[{"label":"主理人","field_name":"master","type":"StringRelatedField"},{"label":"地区","field_name":"region","type":"StringRelatedField"},{"label":"协作人","field_name":"admins","type":"ManyRelatedField"},{"label":"添加时间","field_name":"add_time","type":"DateTimeField"},{"label":"手机号","field_name":"mobile","type":"CharField"},{"label":"姓名","field_name":"name","type":"CharField"},{"label":"新房客源","field_name":"new","type":"BooleanField"},{"label":"二手房客源","field_name":"old","type":"BooleanField"},{"label":"租房客源","field_name":"rent","type":"BooleanField"},{"label":"简要说明","field_name":"desc","type":"CharField"}],"extra":null}下面是具体的使用方法list_classclassCustomListSerializer(serializers.ModelSerializer):master=serializers.StringRelatedField()add_time=serializers.DateTimeField(format='%Y-%m-%d%H:%M:%S')classMeta:model=Customfields=["id","mobile","name","master","new","old","rent","add_time"]custom={"id":{'width':'80'}}这是一个典型的列表读取的序列化器，元数据中规定的输出字段，新增的custom字典表明某个字段的特别约定。字典的健名用字段名表示定义某个字段；value值是一个字典，健名自己定义，反馈到前端后，前端方便进行解析。建议保留width，label这类字段，总之，这里定义什么，前端就会收到什么，然后前端进行渲染。输出到前端后，对应参数健名为：listfilter_class作为筛选器，要定义每个筛选项，这是没办法避免的，例如：classCustomFilter(django_filters.rest_framework.FilterSet):mobile=django_filters.CharFilter(method='keyword_filter',help_text="手机号")name=django_filters.CharFilter(method='keyword_filter',help_text="姓名")desc=django_filters.CharFilter(method='keyword_filter',help_text="说明")new=django_filters.BooleanFilter(method='common_filter',help_text="新房客源",label={'options':get_easy_bool()})old=django_filters.BooleanFilter(method='common_filter',help_text="二手房客源",label={'options':get_easy_bool()})rent=django_filters.BooleanFilter(method='common_filter',help_text="租房客源",label={'options':get_easy_bool()})以上就是对筛选器的定义，对项的定义，放在label进行定义，选择类的给options值，help_text是显示出来的名称输出到前端后，对应参数健名为：filtercreate_class创建类的输出，是为了前端自动构造提交表单而定，序列化器写法没有特别，和list一样，可以在元数据中增加一个custom字典，对应把每个字段的特别要求输送到前端。和其他不同的是，create的Meta下可以额外增加一个tabs，结构为：tabs=[{'label':'基本信息','fields':['mobile','name','region','desc']},{'label':'权限配置','fields':['master','admins','new','old','rent']}]另外，还增加了一个inlines的数据，结构如下inlines={'custom':{'class':TrackCreateSerializer,'api':'/admin/user/track/data/','limit':10}}说明：健名是作为主键的字段名，也就是其他子数据的主字段名；class是这个子数据创建的序列化器；api是创建接口；limit是一次最多多少个这样设定，前端就可以得到这个tabs，可以用于添加表单分步执行。输出到前端后，对应参数健名为：createread_class读取详细内容的序列化器，要注意的是最好不要构建外键对象，而是要用serializers.StringRelatedField()方法把外键进行文本化，这样才能在前端正常显示。输出到前端后，对应参数健名为：readextra这个用于额外给前端一个扩展的数据字段，可以设定一切能够转为json的数据，用于自己定义与前端的协议输入到前端后，对应参数健名为：extra版本历史1.0.5 修复外键选择项过多的问题，添加choices_limit参数，默认超过30个选项就不在从choices里自动取选择项，请在custom里对字段检索方式进行配置1.0.6 对隐藏字段支持屏蔽，同时修复了一下可能出现的bug；增加了数字字段的小数点长度判断1.0.7 增加inline模式的配置数据输出，规则见create_class说明1.0.8 优化了代码，聚类了函数；增加list和read方式下的参数，为patch字段级提供方便1.0.9 修正inlines的bug1.0.10 修正create的bug1.0.11 增加default值的输出1.0.12 修复bug1.1.0  增加update1.1.1  修改默认tabs1.1.2  修复bug1.1.3  修复bug1.1.4  inlines内增加filter参数1.2.0 新增工具包，详见hadmin.utils1.2.1 修复list方法上的bug