-
-
在app的主目录创建serializers.py文件用于保存该应用的序列化器,先使用ModelSerializer快速创建序列化器
-
from rest_framework import serializers
from .models import Student
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__" #全部字段
exclude = ('pub_date')#除了xx字段
read_only_fields = (''字段'')
fields = ('xx','xx')
-
-
编写视图函数
-
from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer
# Create your views here.
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all()
#指明该视图集在查询数据时使用的查询集
serializer_class = StudentModelSerializer
#指明该视图进行序列化或反序列哈使用的序列化器
-
-
定义路由
-
# 路由列表
urlpatterns = []
router = DefaultRouter() # 可以处理视图的路由器
router.register('students', views.StudentViewSet) # 向路由器中注册视图集
urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表中 -
将子路由include到主路由
-
-
为什么ModelViewSet不需要自己写方法,就能实现数据的增删改查
-
ModelViewSet中默认已经有五个混入类直接封装好处理各种请求.对应关系已经访问url如下: DRF默认已经封装了五种常用的方法,但由于封装不同需要在访问的时候注意访问url(detail是否为true).当然也可以根据自己需要重新各种方法
-
类名 HTTP方法 说明 mixins.CreateModelMixin POST 创建数据 mixins.RetrieveModelMixin GET 检索数据 mixins.DestroyModelMixin DELETE 删除数据 mixins.ListModelMixin GET 获取数据 mixins.UpdateModelMixin PUT 更新数据
-
-
自定义序列化器
-
from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
"""学生信息序列化器"""
# 1. 需要进行数据转换的字段
id = serializers.IntegerField()
name = serializers.CharField()
age = serializers.IntegerField()
sex = serializers.BooleanField()
description = serializers.CharField()
# 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息
# 3. 验证代码
# 4. 编写添加和更新模型的代码
-
-
序列化器构造方法
-
Serializer(instance=None, data=empty, **kwarg)
-
序列化时传入instance参数,GET请求将查询到的orm对象序列化为json数据,
-
反序列哈时传入data参数,POST时将接受到的json数据反序列化成orm对象
-
PUT修改操作,既要先查询反序列化要修改的json数据,也要返回序列化修改后的orm对象
-
context传入额外参数
-
-
序列化器基本使用
-
先查询对象
-
from students.models import Student
student = Student.objects.get(id=3)
-
-
构造序列化器
-
from .serializers import StudentSerializer
serializer = StudentSerializer(instance=student)
-
-
获取序列化数据
-
serializer.data
# {'id': 4, 'name': '小张', 'age': 18, 'sex': True, 'description': '猴赛雷'}
-
-
如果是多条数据的查询集QuerySet,在序列化器中添加many=True参数
-
-
反序列化
-
接受到的json数据
-
构造序列化器对象,将data传入
-
进行数据验证
-
from book.serializers import BookSerializer
data = {'pub_date': 123}
serializer = BookSerializer(data=data)
serializer.is_valid() # 返回False
serializer.errors
# {'title': [ErrorDetail(string='This field is required.', code='required')], 'pub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {}
data = {'title': 'python'}
serializer = BookSerializer(data=data)
serializer.is_valid() # True 验证结果返回值
serializer.errors # {} 错误信息
serializer.validated_data # OrderedDict([('btitle', 'python')]) -
is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
-
-
自定义验证行为
-
对字段进行验证,在序列化器类中定义函数
-
def validate_title(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value
-
-
多个字段
-
def validate(self, attrs):
read = attrs['read']
comment = attrs['comment']
if read < comment:
raise serializers.ValidationError('阅读量小于评论量,不可以通过')
return attrs
-
-
validators参数
-
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
序列化器中
title = serializers.CharField(label='名称', max_length=20, validators=[about_django])
-
-
-
数据的保存
-
将数据转化为模型类
-
class BookSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data):
"""新建"""
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
(
-
-
将数据存到数据库
-
class BookSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data):
"""新建"""
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.title = validated_data.get('title', instance.title)
instance.pub_date = validated_data.get('pub_date', instance.pub_date)
instance.read = validated_data.get('read', instance.read)
instance.comment = validated_data.get('comment', instance.comment)
instance.save()
return instance
-
-
实现上述两个方法后,在反序列化时,就可以通过save()方法返回一个数据实例对象了
-
如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。
-
-
以上步骤实现了对数据序列化和反序列化的基本操作
-
以下步骤实现的是对数据的操作,
-
django rest framework中视图函数的作用:控制序列化器的执行,控制数据库的操作
-
视图基类
-
APIView
-
继承与django的View父类
-
传到视图的是Request不是django的HTTPRequest对象,(Response,HTTPResponse)
-
任何的异常都会捕捉到,并处理成合适的响应信息
-
进行dispath()前,会进行身份验证,权限检查,流量控制
-
支持定义的类属性
-
authentication_classes** 列表或元祖,身份认证类
-
permissoin_classes 列表或元祖,权限检查类
-
throttle_classes 列表或元祖,流量控制类
-
-
-
GenericAPIView(通用视图类)rest_framework.generics.GenericAPIView
-
属性和方法
-
serializer_class指明视图使用的序列化器
-
get_serializer_class(self)
-
当一个视图类中调用多个序列化器时,可以通过条件判断在这个方法汇总返回不同的序列化器名称就能使用不同的序列化器
-
get_serializer(self,*args,**kwargs)
-
返回序列化器的对象,主要是给Minxin扩展类使用,在视图中获取序列化器对象
-
-
-
-
数据库查询的属性和方法
-
queryset 指明查询的数据查询集
-
get_quesyset(self):返回视图使用的查询集
-
get_object(self),返回单个对象,需要前端提供PK,url写re_path方法
-
pagination_class** 指明分页控制类
-
filter_backends 指明过滤控制后端
-
-
五个MinXin类
-
ListModelMinXin,快速实现列表视图
-
from rest_framework.mixins import ListModelMixin,Create.......,..
class BookListView(ListModelMixin,Create....,...,.. GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.list(request)
-
-
CreateModelMinXin,快速创建资源
-
def post(self,request):
return self.create(request)
-
-
RetrieveModelMiXin:快速返回一个存在的数据
-
def get(self, request, pk):
return self.retrieve(request,pk)
-
-
UpdateModelMiXin快速更新一个对象
-
def put(self,request,pk):
return self.update(request,pk) -
在调试API接口时使用了post的情况下,put不显示,不影响真正使用,应为前端会指定请求方法来区分不同请求
-
-
DestroyModelMiXin:快速删除一个存在的数据
-
def delete(self,request,pk):
return self.destroy(request,pk)
-
注意,不是delete()方法来删除数据,而是destory(),若是delete的话,就会超出递归最大深度
-
-
-
GenericAPIView的视图子类
-
CreateAPIView
-
ListAPIView
-
RetrieveAPIView
-
DestoryAPIView
-
UpdateAPIView
-
RetrieveUpdateAPIView
-
RetrieveUpdateDestoryAPIView
-
-
-
常用视图集父类
-
ViewSet需要自定义方法,list,create等对应路由asview({})字典中的键的值,需要继承于ViewSetMixin
-
GenericViewSet不需要自定义方法,只需要继承与Mixin的五个扩展类就能实现VIewSet的功能
-
ModelVIewSet,不需要自定义方法,继承与GenericViewSet,也包括Mixin的五个扩展类,所以只需要继承与ModelViewSet就能实现增删改查的API接口
-
from rest_framework.viewsets import ModelViewSet
class BookViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudnetModelSerializer -
path('students7/',views.BookViewSet.as_view({"get":"list",'post':'create'})),
re_path('students7/(?P<pk>d+)',views.BookViewSet.as_view({"get":"retrieve",'put':'update','delete':'destroy'})), -
支持自定义动作
-
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def login(self,request):
"""学生登录功能"""
return Response({"message":"登录成功"}) -
path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))
-
-
视图函数汇中的self.action可以获取请求的方法名,也就是as_view()中字典的键
-
-
-
Routers
-
使用方法
-
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'router_stu', StudentModelViewSet, base_name='student')
效果如下
^books/$ name: book-list
^books/{pk}/$ name: book-detail -
添加路由数据
-
urlpatterns = [
...
]
urlpatterns += router.urls -
urlpatterns = [
...
url(r'^', include(router.urls))
]
-
-
自定义方法生成路由
-
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# methods 设置当前方法允许哪些http请求访问当前视图方法
# detail 设置当前视图方法是否是操作一个数据
# detail为True,表示路径名格式应该为 router_stu/{pk}/login/
@action(methods=['get'], detail=True)
def login(self, request,pk):
"""登录"""
...
# detail为False 表示路径名格式应该为 router_stu/get_new_5/
@action(methods=['put'], detail=False)
def get_new_5(self, request):
"""获取最新添加的5个学生信息""" -
^router_stu/get_new_5/$ name: router_stu-get_new_5
^router_stu/{pk}/login/$ name: router_stu-login -
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。SImpleRouter就返回错误页面,或者BUG调试的错误页面
-
-
-
-
认证,对请求身份的认证,常用的是session和token
-
可以在配置文件settings.py,中配置全局默认的认证方案
-
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
)
}
-
-
也可以在视图中通过设置authentication_classess属性来设置
-
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
)
}
-
-
-
权限permissions
-
权限控制可以限制用户对于视图的访问和对于具体数据的访问
-
全局配置
-
REST_FRAMEWORK = {
....
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
-
-
不指明则默认配置
-
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
)
-
-
局部配置
-
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
...
-
-
权限类别
-
AllowAny 允许所有用户
-
IsAuthenticated 仅通过认证的用户
-
IsAdminUser 仅管理员用户
-
IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的游客只能查看数据。
-
-
自定义权限
-
自定义权限需要继承于rest_framework.permissions.BasePeomission父类,并实现两个任一方法之一或全部
-
.has_permission(self,request,view):可以访问视图
-
has_object_permission(self,request,view,obj):是否可访问对象
-
from rest_framework.permissions import BasePermission
class IsXiaoMingPermission(BasePermission):
def has_permission(self, request, view):
if( request.user.username == "xiaoming" ):
return True -
from .permissions import IsXiaoMingPermission
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
permission_classes = [IsXiaoMingPermission]
-
-
-
-
限流
-
可以在配置文件中,使用
DEFAULT_THROTTLE_CLASSES
和DEFAULT_THROTTLE_RATES
进行配置, -
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
DEFAULT_THROTTLE_RATES` 可以使用 `second`, `minute`, `hour` 或`day`来指明周期。 -
具体是图中
-
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class ExampleView(APIView):
throttle_classes = (UserRateThrottle,)
...
-
-
可选的限流类
-
1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用
DEFAULT_THROTTLE_RATES['anon']
来设置频次2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用
DEFAULT_THROTTLE_RATES['user']
来设置频次3)ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
例如:
-
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
-
-
实例
-
'DEFAULT_THROTTLE_RATES': {
'anon': '3/minute',
'user': '10/minute'
} -
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle
class StudentAPIView(RetrieveAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
throttle_classes = (UserRateThrottle,)
-
-
-
过滤Filtering
-
pip install django-filter
-
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
} -
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_fields = ('age', 'sex')
# 127.0.0.1:8000/four/students/?sex=1 -
这样url携带的指定过滤字段的参数就起到了过滤功能
-
http://127.0.0.1:8000/student/?age=55,就只查出age=55的数据
-
-
排序
-
OrderingFilter过滤器进行指定字段的排序
-
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
filter_backends = [OrderingFilter]
ordering_fields = ('id', 'age')
# 127.0.0.1:8000/books/?ordering=-age
# -id 表示针对id字段进行倒序排序
# id 表示针对id字段进行升序排序 -
from rest_framework.generics import ListAPIView
from students.models import Student
from rest_framework.filters import OrderingFilter#不要用django_filters中额OrderingFilter类,用这个哦
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend
class Student3ListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
filter_fields = ('age', 'sex')
# 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
# 否则过滤功能会失效
filter_backends = [OrderingFilter,DjangoFilterBackend]
ordering_fields = ('id', 'age')
-
-
分页器Pagenation
-
全局配置
-
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页数目
}
-
-
自定义
-
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
class BookDetailView(RetrieveAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
pagination_class = LargeResultsSetPagination
-
-
关闭分页
-
pagination_class = None
-
-
分页器分类
-
PageNumberPagination
-
page_size 每页数目
-
page_query_param 前端发送的页数关键字名,默认为"page"
-
page_size_query_param 前端发送的每页数目关键字名,默认为None
-
max_page_size 前端最多能设置的每页数量
-
# 声明分页的配置类
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
# 默认每一页显示的数据量
page_size = 2
# 允许客户端通过get参数来控制每一页的数据量
page_size_query_param = "size"
max_page_size = 10
# 自定义页码的参数名
page_query_param = "p"
class StudentAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
pagination_class = StandardPageNumberPagination
# 127.0.0.1/four/students/?p=1&size=5
-
LimitOffsetPagination
-
default_limit 默认限制,默认值与
PAGE_SIZE
设置一直 -
limit_query_param limit参数名,默认'limit'
-
offset_query_param offset参数名,默认'offset'
-
max_limit 最大limit限制,默认None
-
from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
# 默认每一页查询的数据量,类似上面的page_size
default_limit = 2
limit_query_param = "size"
offset_query_param = "start"
class StudentAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# 调用页码分页类
# pagination_class = StandardPageNumberPagination
# 调用查询偏移分页类
pagination_class = StandardLimitOffsetPagination
-
-
-
异常处理
-
自定义,建议在settings的同级目录下创建utils工具包,创建execptions.py异常处理的代码文件
-
from rest_framework.views import exception_handler
from rest_framework import status
from rest_framework.response import Response
from django.db import DatabaseError
def student_exception(exc,context):
'''自定义异常优先调用drf本身的异常处理,返回要么时None,要么是response'''
response = exception_handler(exc, context)
if response is None:
view = context['view']
if isinstance(exc, DatabaseError):
print('[%s]: %s' % (view, exc))
response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response -
配置文件
-
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
} -
不声明默认是
-
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
} -
种类
-
- APIException 所有异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限决绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
-
-
-
自动生成接口文档
-
pip install coreapi
-
在总路由中添加接口文档路径。
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
path('docs/', include_docs_urls(title='站点页面标题'))
] -
使用
-
class BookListView(generics.ListAPIView): """ 返回所有图书信息. """
-
class BookListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有图书信息.
post:
新建图书.
""" -
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
""" -
127.0.0.1:8000/docs/
-
-
-
Xadmin
-
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
-
INSTALLED_APPS = [
...
'xadmin',
'crispy_forms',
'reversion',
...
]
# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'
# 修改时区
TIME_ZONE = 'Asia/Shanghai' -
python manage.py makemigrations python manage.py migrate
-
import xadmin
xadmin.autodiscover()
# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()
urlpatterns = [
path(r'xadmin/', xadmin.site.urls),
] -
python manage.py createsuperuser
-
在子应用中创建adminx.py文件。注意不是xadmin.py文件,不识别的
-
import xadmin
from xadmin import views
class BaseSetting(object):
"""xadmin的基本配置"""
enable_themes = True # 开启主题切换功能
use_bootswatch = True
xadmin.site.register(views.BaseAdminView, BaseSetting)
class GlobalSettings(object):
"""xadmin的全局配置"""
site_title = # 设置站点标题
site_footer = # 设置站点的页脚
menu_style = "accordion" # 设置菜单折叠
xadmin.site.register(views.CommAdminView, GlobalSettings)
-
-
站点Model管理
xadmin可以使用的页面样式控制基本与Django原生的admin一直。
-
list_display 控制列表展示的字段
list_display = ['id', 'btitle', 'bread', 'bcomment']
-
search_fields 控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询
search_fields = ['id','btitle']
-
list_filter 可以进行过滤操作的列,对于分类、性别、状态
list_filter = ['is_delete']
-
ordering 默认排序的字段
-
show_detail_fields 在列表页提供快速显示详情信息
-
list_editable 在列表页可以快速直接编辑的字段
-
refresh_times 指定列表页的定时刷新
refresh_times = [5, 10,30,60] # 设置允许后端管理人员按多长时间(秒)刷新页面
-
list_export 控制列表页导出数据的可选格式
list_export = ('xls', 'xml', 'json') list_export设置为None来禁用数据导出功能
list_export_fields = ('id', 'btitle', 'bpub_date') -
show_bookmarks 控制是否显示书签功能
show_bookmarks = True
-
data_charts 控制显示图表的样式
data_charts = {
"order_amount": {
'title': '图书发布日期表',
"x-field": "bpub_date",
"y-field": ('btitle',),
"order": ('id',)
},
# 支持生成多个不同的图表
# "order_amount": {
# 'title': '图书发布日期表',
# "x-field": "bpub_date",
# "y-field": ('btitle',),
# "order": ('id',)
# },
}-
title 控制图标名称
-
x-field 控制x轴字段
-
y-field 控制y轴字段,可以是多个值
-
order 控制默认排序
-
-
model_icon 控制菜单的图标【图标的设置可以参考bootstrap的图标css名称】
class BookInfoAdmin(object):
model_icon = 'fa fa-gift'
xadmin.site.register(models.BookInfo, BookInfodmin) -
readonly_fields 在编辑页面的只读字段
-
exclude 在编辑页面隐藏的字段
-
-
-