视图家族 & 路由组件

视图基类:APIView、GenericAPIView

视图工具类:mixins包下的五个类(六个方法)

工具视图类:generics包下的所有GenericAPIView的子类

视图集:viewsets包下的类

GenericAPIView基类(基本不会单独使用,了解即可,但是是高级视图类的依赖基础)
1)GenericAPIView继承APIView,所有APIView子类写法在继承GenericAPIView时可以保持一致
2)GenericAPIView给我们提供了三个属性 queryset、serializer_class、lookup_field
3)GenericAPIView给我们提供了三个方法 get_queryset、get_serializer、get_obj

mixins包存放了视图工具类(不能单独使用,必须配合GenericAPIView使用)

​```
CreateModelMixin:单增工具类
create方法 ListModelMixin:群查工具类
list方法 RetrieveModelMixin:单查工具类
retrieve方法 UpdateModelMixin:单整体局部改工具类
update方法 DestroyModelMixin:单删工具类
destory方法
generics包下的所有GenericAPIView的子类(就是继承GenericAPIView和不同mixins下的工具类的组合)

​```python
"""
1)定义的视图类,继承generics包下已有的特点的GenericAPIView子类,可以在只初始化queryset和serializer_class两个类属性后,就获得特定的功能 2)定义的视图类,自己手动继承GenericAPIView基类,再任意组合mixins包下的一个或多个工具类,可以实现自定义的工具视图类,获得特定的功能或功能们 注:
i)在这些模式下,不能实现单查群查共存(可以加逻辑区分,也可以用视图集知识)
ii)DestroyModelMixin工具类提供的destory方法默认是从数据库中删除数据,所以一般删除数据的需求需要自定义逻辑
"""

urls.py

from django.conf.urls import url
from . import views urlpatterns = [
# ... url(r'^v1/books/$', views.BookV1APIView.as_view()),
url(r'^v1/books/(?P<pk>\d+)/$', views.BookV1APIView.as_view()), url(r'^v2/books/$', views.BookV2APIView.as_view()),
url(r'^v2/books/(?P<pk>\d+)/$', views.BookV2APIView.as_view()), url(r'^v3/books/$', views.BookV3APIView.as_view()),
url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view()),
]

views.py

# ----------------------------- 过渡写法:了解 -----------------------------

from rest_framework.generics import GenericAPIView
class BookV1APIView(GenericAPIView):
# 将数据和序列化提示为类属性,所有的请求方法都可以复用
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer
lookup_field = 'pk' # 可以省略,默认是pk,与url有名分组对应的 # 群查
def get(self, request, *args, **kwargs):
# queryset = models.Book.objects.filter(is_delete=False).all() # => 方法+属性两行代码
queryset = self.get_queryset()
# serializer = serializers.BookModelSerializer(instance=queryset, many=True) # => 方法+属性两行代码
serializer = self.get_serializer(instance=queryset, many=True)
return APIResponse(results=serializer.data) # 单查
# def get(self, request, *args, **kwargs):
# obj = self.get_object()
# serializer = self.get_serializer(obj)
# return APIResponse(results=serializer.data) # 单增
def post(self, request, *args, **kwargs):
# serializer = serializers.BookModelSerializer(data=request.data)
serializer = self.get_serializer(data=request.data) # 同样的步骤多了,好处就来了
serializer.is_valid(raise_exception=True)
obj = serializer.save()
return APIResponse(result=self.get_serializer(obj).data, http_status=201) # ----------------------------- 过渡写法:了解 ----------------------------- from rest_framework.generics import GenericAPIView
from rest_framework import mixins
class BookV2APIView(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer # 单查
def get(self, request, *args, **kwargs):
# obj = self.get_object()
# serializer = self.get_serializer(obj)
# return APIResponse(results=serializer.data) # return self.retrieve(request, *args, **kwargs) response = self.retrieve(request, *args, **kwargs)
return APIResponse(result=response.data) # 单增
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) # ----------------------------- 开发写法:常用 ----------------------------- from rest_framework.generics import RetrieveAPIView
class BookV3APIView(RetrieveAPIView):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer # 单查
pass

视图集与路由组件

准备工作

models.py

from django.db import models

# 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
updated_time = models.DateTimeField(auto_now_add=True) class Meta:
abstract = True # 必须完成该配置 class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
image = models.ImageField(upload_to='img', default='img/default.png') publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False) @property # @property字段默认就是read_only,且不允许修改
def publish_name(self):
return self.publish.name @property # 自定义序列化过程
def author_list(self):
temp_author_list = []
for author in self.authors.all():
author_dic = {
"name": author.name
}
try:
author_dic['phone'] = author.detail.phone
except:
author_dic['phone'] = ''
temp_author_list.append(author_dic)
return temp_author_list class Publish(BaseModel):
name = models.CharField(max_length=64) class Author(BaseModel):
name = models.CharField(max_length=64) class AuthorDetail(BaseModel):
phone = models.CharField(max_length=11)
author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, on_delete=models.CASCADE)

serializers.py

from rest_framework import serializers
from . import models # 只有在资源需要提供群改,才需要定义ListSerializer,重写update方法
class BookListSerializer(serializers.ListSerializer):
def update(self, queryset, validated_data_list):
return [
self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
] class BookModelSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = BookListSerializer
model = models.Book
fields = ['name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list']
extra_kwargs = {
'publish': {
'write_only': True
},
'authors': {
'write_only': True
}
}

基于 GenericAPIView 的十大接口

views.py

# 十大接口:
# 1)单查、群查、单增、单整体改、单局部改都可以直接使用
# 2)单删不能直接使用,因为默认提供的功能是删除数据库数据,不是我们自定义is_delete字段值修改
# 3)除了群查以为的接口,都要自己来实现 # 注:给序列化类context赋值{'request': request},序列化类就可以自动补全后台图片链接
from rest_framework.generics import GenericAPIView
from rest_framework import mixins
from . import models, serializers
from rest_framework.response import Response class BookV1APIView(GenericAPIView,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin): queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.retrieve(request, *args, **kwargs) # 单查 # queryset = models.Book.objects.filter(is_delete=False).all()
# 注:给序列化类context赋值{'request': request},序列化类就可以自动补全后台图片链接
# serializer = serializers.BookModelSerializer(queryset, many=True, context={'request': request})
# return Response(serializer.data)
return self.list(request, *args, **kwargs) # 群查 def post(self, request, *args, **kwargs):
if not isinstance(request.data, list):
return self.create(request, *args, **kwargs) serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=201, headers=headers) def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
pks = [pk]
else:
pks = request.data
try:
rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
except:
return Response(status=400)
if rows:
return Response(status=204)
return Response(status=400) def put(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.update(request, *args, **kwargs) pks = []
try:
for dic in request.data:
pks.append(dic.pop('pk'))
objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
assert len(objs) == len(request.data)
except:
return Response(status=400)
serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
serializer.is_valid(raise_exception=True)
objs = serializer.save()
return Response(serializers.BookModelSerializer(objs, many=True).data) def patch(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.partial_update(request, *args, **kwargs) pks = []
try:
for dic in request.data:
pks.append(dic.pop('pk'))
objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
assert len(objs) == len(request.data)
except:
return Response(status=400)
serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True, partial=True)
serializer.is_valid(raise_exception=True)
objs = serializer.save()
return Response(serializers.BookModelSerializer(objs, many=True).data)

总结:

配置queryset、serializer_class、lookup_field

要自己定义get、post等方法,内部调用retrieve、create方法

基于 generics 包下工具视图类的六大基础接口

views.py

# 六大基础接口
# 1)直接继承generics包下的工具视图类,可以完成六大基础接口
# 2)单查群查不能共存
# 3)单删一般会重写
from . import models, serializers
from rest_framework.response import Response
from rest_framework import generics
class BookV2APIView(generics.ListAPIView,
generics.RetrieveAPIView,
generics.CreateAPIView,
generics.UpdateAPIView,
generics.DestroyAPIView):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.retrieve(request, *args, **kwargs)
return self.list(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
return Response(status=204)

总结:

配置queryset、serializer_class、lookup_field

重写get处理单查群查共存即可

delete方法是否重写看需求

视图集

解析actions,修改 请求分发 - 响应函数 映射关系

""" ViewSetMixin类存在理由推到
1)工具视图类,可以完成应付六大基础接口,唯一缺点是单查与群查接口无法共存
(list, retrieve, create, update, destory)
(只配置queryset、serializer_class、lookup_field)
2)不能共存的原因:RetrieveAPIView和ListAPIView都是get方法,不管带不带pk的get请求,只能映射给一个get方法
3)能不能修改映射关系:
比如将/books/的get请求映射给list方法,
将/books/(pk)/的get请求映射给retrieve方法,
甚至可以随意自定义映射关系
""" """ 继承视图集的视图类的as_view都是走ViewSetMixin类的,核心源码分析
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
# ... # 没有actions,也就是调用as_view()没有传参,像as_view({'get': 'list'})
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`") # ... # 请求来了走view函数
def view(request, *args, **kwargs):
# ...
# 解析actions,修改 请求分发 - 响应函数 映射关系
self.action_map = actions
for method, action in actions.items(): # method:get | action:list
handler = getattr(self, action) # 从我们视图类用action:list去反射,所以handler是list函数,不是get函数
setattr(self, method, handler) # 将get请求对应list函数,所以在dispath分发请求时,会将get请求分发给list函数 # ...
# 通过视图类的dispatch完成最后的请求分发
return self.dispatch(request, *args, **kwargs) # ...
# 保存actions映射关系,以便后期使用
view.actions = actions
return csrf_exempt(view)
"""

核心

url(r'^v3/books/$', views.BookV3APIView.as_view(
{'get': 'list', 'post': 'create', 'delete': 'multiple_destroy'}
)), url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view(
{'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}
)),

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
class BookV3APIView(ModelViewSet):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer # 可以在urls.py中as_view({'get': 'my_list'})自定义请求映射
def my_list(self, request, *args, **kwargs):
return Response('ok') # 需要完成字段删除,不是重写delete方法,而是重写destroy方法
def destroy(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
return Response(status=204) # 群删接口
def multiple_destroy(self, request, *args, **kwargs):
try:
models.Book.objects.filter(is_delete=False, pk__in=request.data).update(is_delete=True)
except:
return Response(status=400)
return Response(status=204)

总结:

  1. ViewSetMixin 类:重写 as_view 方法

    作用:as_view({"get": "list"}) 来自定义请求与响应的映射关系

  2. 两个视图集基类:

    ViewSet: 与 Model 关系不是特别紧密

    GenericViewSet : 与 Model 关系特别紧

  3. 两个GenericViewSet的子类:

    ModelViewSet:六大基础接口共存

    ReadOnlyModelViewSet:只读接口

    注:都只需要配置queryset、serializer_class、lookup_field;根据需求决定是否重写某些方法

  4. 自己用两个视图集基类与 mixins 包形成自定义组合

路由组件:必须配合视图集使用

urls.py

from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter() # 以后就写视图集的注册即可:BookV3APIView和BookV4APIView都是视图集
router.register('v3/books', views.BookV3APIView, 'book')
router.register('v4/books', views.BookV4APIView, 'book') urlpatterns = [
url('', include(router.urls))
]

views.py

from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer

自定义路由组件(了解)

router.py

from rest_framework.routers import SimpleRouter as DrfSimpleRouter
from rest_framework.routers import Route, DynamicRoute class SimpleRouter(DrfSimpleRouter):
routes = [
# List route.
Route(
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'list',
'post': 'create', # 注:群增只能自己在视图类中重写create方法,完成区分
'delete': 'multiple_destroy', # 新增:群删
'put': 'multiple_update', # 新增:群整体改
'patch': 'multiple_partial_update' # 新增:群局部改
},
name='{basename}-list',
detail=False,
initkwargs={'suffix': 'List'}
),
# Dynamically generated list routes. Generated using
# @action(detail=False) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=False,
initkwargs={}
),
# Detail route.
Route(
url=r'^{prefix}/{lookup}{trailing_slash}$',
mapping={
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
},
name='{basename}-detail',
detail=True,
initkwargs={'suffix': 'Instance'}
),
# Dynamically generated detail routes. Generated using
# @action(detail=True) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
),
]

urls.py

from .router import SimpleRouter
router = SimpleRouter() router.register('car', views.CarAPIView, 'car') urlpatterns = [
url('', include(router.urls))
]

上传图片接口

urls.py

from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter() # /books/image/(pk) 提交 form-data:用image携带图片
router.register('books/image', views.BookUpdateImageAPIView, 'book') urlpatterns = [
url('', include(router.urls))
]

serializers.py

class BookUpdateImageModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['image']

views.py

# 上次文件 - 修改头像 - 修改海报
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
class BookUpdateImageAPIView(GenericViewSet, mixins.UpdateModelMixin):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookUpdateImageModelSerializer

总结:

上传图片:前台提交form-data,类型选择文件类型,后台用request.data和request.FILES都可以访问

权限

models.py

from django.db import models

# RBAC - Role-Based Access Control
# Django的 Auth组件 采用的认证规则就是RBAC from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True) def __str__(self):
return self.username class Book(models.Model):
name = models.CharField(max_length=64) def __str__(self):
return self.name class Car(models.Model):
name = models.CharField(max_length=64) def __str__(self):
return self.name

settings.py

# 自定义User表,要配置
AUTH_USER_MODEL = 'api.User'

admin.py

from django.contrib import admin
from . import models from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin # 自定义User表后,admin界面管理User类
class UserAdmin(DjangoUserAdmin):
# 添加用户课操作字段
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2', 'is_staff', 'mobile', 'groups', 'user_permissions'),
}),
)
# 展示用户呈现的字段
list_display = ('username', 'mobile', 'is_staff', 'is_active', 'is_superuser') admin.site.register(models.User, UserAdmin)
admin.site.register(models.Book)
admin.site.register(models.Car)
# 1)像专门做人员权限管理的系统(CRM系统)都是公司内部使用,所以数据量都在10w一下,一般效率要求也不是很高
# 2)用户量极大的常规项目,会分两种用户:前台用户(三大认证) 和 后台用户(BRAC来管理)
# 结论:没有特殊要求的Django项目可以直接采用Auth组件的权限六表,不需要自定义六个表,也不需要断开表关系,但可能需要自定义User表

做项目是否要分表管理前后台用户

"""
1)是否需要分表
答案:不需要
理由:前后台用户共存的项目,后台用户量都是很少;做人员管理的项目,基本上都是后台用户;前后台用户量都大的会分两个项目处理 2)用户权限六表是否需要断关联
答案:不需要
理由:前台用户占主导的项目,几乎需求只会和User一个表有关;后台用户占主导的项目,用户量不会太大 3)Django项目有没有必须自定义RBAC六表
答案:不需要
理由:auth组件功能十分强大且健全(验证密码,创建用户等各种功能);admin、xadmin、jwt、drf-jwt组件都是依赖auth组件的(自定义RBAC六表,插件都需要自定义,成本极高)
"""

权限六表

RBAC-Role-Based Access Control

三表

三大认证规则

总结

# 1)后台用户对各表操作,是后台项目完成的,我们可以直接借助admin后台项目(Django自带的)
# 2)后期也可以用xadmin框架来做后台用户权限管理 # 3)前台用户的权限管理如何处理
# 定义了一堆数据接口的视图类,不同的登录用户是否能访问这些视图类,能就代表有权限,不能就代表无权限
# 前台用户权限用drf框架的 三大认证 # 注:前台用户权限会基于 jwt 认证

视图家族 & 路由组件的更多相关文章

  1. drf4 视图与路由组件

    APIView和View的区别 不管是View还是APIView最开始调用的都是as_view() APIView继承了View, 并且执行了View中的as_view()方法,最后把view返回了, ...

  2. drf二次封装response-APIViews视图家族-视图工具集-工具视图-路由组件

    视图类传递参数给序列化类 (1).在视图类中实例化 序列化对象时,可以设置context内容. (2).在序列化类中的局部钩子.全局钩子.create.update方法中,都可以用self.conte ...

  3. VueJs(11)---vue-router(命名路由,命名视图,重定向别名,路由组件传参)

    vue-router 上篇文章讲了第一篇vue-router相关文章,文章地址:VueJs(10)---vue-router(进阶1) 一.命名路由 有时候,通过一个名称来标识一个路由显得更方便一些, ...

  4. DRF 视图组件,路由组件

    视图组件  -- 第一次封装   -- GenericAPIView(APIView):    queryset = None    serializer_class = None    def ge ...

  5. drf序列化组件之视图家族

    一.视图家族的分类 1.导入分类 from rest_framewok import views, generics, mixins, viewsets views:视图类 ​ 两大视图类:APIVi ...

  6. DRF 视图家族及路由层补充

    目录 视图家族 一.views视图类 1.APIView类 2.GenericAPIView类(generics中) 二.mixins类:视图辅助工具 1.RetrieveModelMixin 2.L ...

  7. drf框架 - 视图家族 | GenericAPIView | mixins | generics | viewsets

    视图家族 view:视图 generics:工具视图 mixins:视图工具集 viewsets:视图集 学习曲线: APIView => GenericAPIView => mixins ...

  8. drf序列化高级、自定义只读只写、序列化覆盖字段、二次封装Response、数据库查询优化(断关联)、十大接口、视图家族

    目录 自定义只读 自定义只写 序列化覆盖字段 二次封装Response 数据库关系分析 断外键关联关系 ORM操作外键关系 ORM四种关联关系 基表 系列化类其他配置(了解) 十大接口 BaseSer ...

  9. 二次封装 Reponse,视图家族

    复习 """ 1.整体修改与局部修改 # 序列化 ser_obj = ModelSerializer(model_obj) # 反序列化,save() => cre ...

随机推荐

  1. JS的起源和发展

    JS概述 JS主要由三部分组成 ECMAScript BOM DOM 对于ECMAScript的理解 这是JS这个大厦的地基和骨架,是核心的部分 BOM:提供与浏览器进行交互的方法和接口 DOM;提供 ...

  2. Python - 查看类的方法和属性,dir(),help()

    1. dir()查看类的方法和属性 查看slice类的方法和属性 dir(slice) 2.help() 查看某个方法的文档 查看slice类中的indices方法 help(slice.indice ...

  3. 用纯css实现双边框效果

    1. box-shadow:0 0 0 1px #feaa9e,0 0 0 5px #fd696f 2. border:1px solid #feaa9e; outline:5px solid #fd ...

  4. vmware虚拟机linux添加硬盘后先分区再格式化操作方法

    先在虚拟机里填加硬盘,如图. 进入linux后台,df-l ,没有显示sdc盘,更切换的是,在fdisk中,却有sdc 看fdisk -l,确实有sdc. 说明sdc还没有分区,也没有格式化,也没有挂 ...

  5. pl/sql修改data

    1,对于语句要包含rowid!

  6. Executor、Executors、ExecutorService多线程操作

    Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command),该方法接收一个Runable实例,它用来执行一 ...

  7. 吴裕雄 python 神经网络——TensorFlow 队列操作

    import tensorflow as tf q = tf.FIFOQueue(2, "int32") init = q.enqueue_many(([0, 10],)) x = ...

  8. Update(Stage5):Kudu_javaApi使用_Spark整合

    Table of Contents: 2.3. 安装 Zookeeper 2.4. 安装 Hadoop 2.4. 安装 MySQL 2.5. 安装 Hive 2.6. 安装 Kudu 2.7. 安装 ...

  9. 【PAT甲级】1064 Complete Binary Search Tree (30 分)

    题意:输入一个正整数N(<=1000),接着输入N个非负整数(<=2000),输出完全二叉树的层次遍历. AAAAAccepted code: #define HAVE_STRUCT_TI ...

  10. Java自学-集合框架 ArrayList和HashSet的区别

    Java ArrayList和HashSet的区别 示例 1 : 是否有顺序 ArrayList: 有顺序 HashSet: 无顺序 HashSet的具体顺序,既不是按照插入顺序,也不是按照hashc ...