视图家族

视图家族在rest_framework源码位置和学习曲线为:

rest_framework.views: 基本视图(APIView)

rest_framework.generics: 工具视图(GenericAPIView)

rest_framework.mixins: 视图工具集(Create/Destroy/List/Retrieve/Update)

rest_framework.viewsets: 视图集

APIView

前几章代码都是基于APIView写的, 这里就不赘述了, 简单来说APIView做了三件事:

1. 重写了as_view()方法, 在as_view()中主要调用了父类的as_view()方法 view = super().as_view(**initkwargs) , 并在最后返回时, 加上了csrf验证 return csrf_exempt(view)

2. 重写了dispatch()方法, 在父类的as_view()中被调用, 这是DRF的真正核心主函数, 在dispatch()中实现了各大模块

GenericAPIView

源码为: rest_framework.generics.GenericAPIView

class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
queryset = None
serializer_class = None # If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None # The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS # The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS def get_queryset(self):
.......

1. 继承与APIView, 保留了APIView的特性, 并在其基础上定义了一下更加方便易用的属性

2. 定义了很多类属性, 如queryset(ORM查找结果集), serializer_class(序列化类), lookup_field(主键字段变量名),

3. 对于上述属性定义了对应的get_xxx方法, 如get_queryset(获取结果查找集), get_serializer(获取序列化类), get_object(获取模型对象)等等

如何使用GenericAPIView

让我们先回顾一下APIView如何实现查询接口的

class BookAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
books = Book.objects.get(pk=pk)
many = False
else:
# 群查
books = Book.objects.filter(is_delete=False).all()
many = True
serializer = BookSerializer(instance=books, many=many)
return MyResponse(result=serializer.data)

可以看到一般查询接口分为3步:

1. 获取结果查找集books

2. 序列化查询结果集得到序列化对象serializer

3. 返回响应

GenericAPIView就是将这通用的三步, 进行封装, 让使用者只需要编写关键的变量值即可

将上面的代码转化为GenericAPIView就是:

class BookGenericAPIView(GenericAPIView):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
# 单取
return self.retrieve(request, *args, **kwargs)
# 群取
return self.list(request, *args, **kwargs) # 单取
def retrieve(self, request, *args, **kwargs):
# books = Book.objects.filter(is_delete=False, pk=pk).first()
books = self.get_object()
# serializer = BookSerializer(instance=books)
serializer = self.get_serializer(instance=books)
return MyResponse(result=serializer.data) # 群取
def list(self, request, *args, **kwargs):
# books = Book.objects.filter(is_delete=False).all()
books = self.get_queryset()
# serializer = BookSerializer(instance=books, many=True)
serializer = self.get_serializer(instance=books, many=True)
return MyResponse(result=serializer.data)

这里将单取和群取做成了两个独立的函数, 且这样看似使用GenericAPIView时代码似乎比使用APIView更多, 但是先不要着急, 我们可以配合后面的mixin一起使用时就不需要写这么多代码了

使用GenericAPIView时, 将原先使用APIView的写法进行了如下替换:

1. 定义了两个变量, queryset用于保存查询结果集, serializer用于保存序列化类

2. 将原获取查询结果集的ORM写法  books = Book.objects.filter(is_delete=False).all() 换成了 books = self.get_queryset() 或 books = self.get_object() , 这里需要注意的一点就是在get_object()内部, 默认是通过参数名'pk'获取具体的主键ID, 而这就需要在url中的参数名也要为'pk', 若url中参数名为'xxx', 则需要重写GenericAPIView中的lookup_field = 'xxx'

3. 将原通过实例化序列化类写法  serializer = BookSerializer(instance=books) 换成了 serializer = self.get_serializer(instance=books)

其中get_queryset()和get_serializer()其实就是去拿到类属性queryset和Serializer_class, 然后再进行实例化操作, 即将原来APIView的写法拆成了两步: 第一步, 定义类属性; 第二步, 获取类属性并实例化.

Mixins工具集

上面手动简单实现了GenericAPIView的单查retrieve和群查list, 而mixins的工具集中就已经封装好了对应的函数, 且功能更加强大, 源码位置为: rest_framework.mixins.py

mixins中主要定义了5个类: CreateModelMixin(用于创建模型类对象), DestroyModelMixin(用于删除模型类对象), ListModelMixin(用于群查模型类对象), RetrieveModelMixin(用于单查模型类对象), UpdateModelMixin(用于单增模型类对象)

Mixins的使用

我们先跟着上面的查询接口, 看一下ListModelMixin用法, 查看ListModelMixin源码可以看到, 其群查的list方法和我们上面手动写的list方法很像, 只是多加了过滤器和分页器的功能

class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# 过滤器
queryset = self.filter_queryset(self.get_queryset()) # 分页器
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

具体用法为:

class BookMixinGenericAPIView(ListModelMixin, GenericAPIView):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
# 群查
# mixins提供的list方法的响应对象是Response, 想将该对象格式化为自定的MyResponse
# response的数据都存放在response.data中
response = self.list(self, request, *args, **kwargs)
return MyResponse(result=response.data)

可以看到mixin工具类和GenericAPIView组合使用时, 实现群查接口非常方便, 只需要三步:

1. 继承ListModelMixin和GenericAPIView

2. 定义类属性queryset和Serializer_class

3. 调用list方法并返回response

同样想要实现单查的话, 再继承一下RetrieveModelMixin, 然后手动分配一下get的方法调用即可, 需要注意的是继承时, 先写mixins的类, 最后写GenericAPIView, 因为是在mixin中调用了GenericAPIView的方法

class BookMixinGenericAPIView(mixins.ListModelMixin, mixins.RetrieveModelMixin,
GenericAPIView):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
response = self.retrieve(request, *args, **kwargs)
else:
# 群查
response = self.list(self, request, *args, **kwargs)
# mixins提供的list方法的响应对象是Response, 想将该对象格式化为自定的MyResponse
# response的数据都存放在response.data
return MyResponse(result=response.data)

同理, mixins还提供了其他的单增/单删/单整体改/单局部改的接口, 完整案例如下:

from rest_framework.generics import GenericAPIView
from rest_framework import mixins
from utils.response import MyResponse
from books.models import Book
from books.serializers import BookSerializer class BookMixinGenericAPIView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin,
mixins.DestroyModelMixin, mixins.UpdateModelMixin,
GenericAPIView):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer # 查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
response = self.retrieve(request, *args, **kwargs)
else:
# 群查
response = self.list(self, request, *args, **kwargs)
# mixins提供的list方法的响应对象是Response, 想将该对象格式化为自定的MyResponse
# response的数据都存放在response.data
return MyResponse(result=response.data) # 单增
def post(self, request, *args, **kwargs):
response = self.create(request, *args, **kwargs)
return MyResponse(result=response.data) # 单删
def delete(self, request, *args, **kwargs):
response = self.destroy(request, *args, **kwargs)
return MyResponse(result=response.data) # 单整体改
def put(self, request, *args, **kwargs):
response = self.update(request, *args, **kwargs)
return MyResponse(result=response.data) # 单局部改
def patch(self, request, *args, **kwargs):
response = self.partial_update(request, *args, **kwargs)
return MyResponse(result=response.data)

mixins的钩子函数

在上面我们调用了mixin的self.create/destroy/update方法实现增删改, 但如果我们对增删改操作还有一些附加的操作(比如在新增时触发发送邮件功能)或者不想用其默认的方法(比如它原来的删除确实是把数据直接删除掉了, 而我们想要的删除只是改一下数据的is_dalete状态)时, mixins给我们提供了相应的钩子函数perform_create/perform_destroy/perform_update, 这些函数都在源码相应的类中定义了:

def perform_create(self, serializer):
serializer.save() def perform_destroy(self, instance):
instance.delete() def perform_update(self, serializer):
serializer.save()

现在我们在我们的视图类中重写一下删除的操作perform_destroy, 将数据的is_dalete状态改为True

    # 单删
def perform_destroy(self, instance):
instance.is_delete = True
instance.save() def delete(self, request, *args, **kwargs):
response = self.destroy(request, *args, **kwargs)
return MyResponse(result=response.data)

generics

上面我们把GenericAPIView和mixins的工具集联合使用, 同时继承, 可以实现简单的增删改查接口, 在generics.py中, 除了提供了GenericAPIView外, 还提供了一些其他的类, 如 CreateAPIView/DestroyAPIView/ UpdateAPIView/ ListCreateAPIView/ RetrieveDestroyAPIView 等等, 这些类都是上我们上面同时继承mixin和GenericAPIView一样, 是帮我们封装好的一些工具类, 源码如下:

# 单增
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) # 群查
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) # 单查
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) # 单删
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) # 单改
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs) # 群查和单增
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) # 单查和单改
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs) # 单查和单删
class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) # 单查和单改和单删
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)

viewsets

在使用了上面的mixin工具后, 会发现增删改查的函数名(create/destroy/partial_update/update/list/retrieve)与对应的请求方式名(post/delete/patch/put/get)两者的命名并不是一样的, 而是存在一个对应关系或者映射关系.我们把两者结合的方式就是在视图类中通过dispatch的反射而定义与请求方式名一样的函数(post/delete/patch/put/get), 在函数中调用mixin的对应的方法.

ViewSetMixin

那么这种结合方式或者对应关系, 能不能写的更加简单一点呢? 在rest_framework.viewsets.py中, 有一个 ViewSetMixin 类, 这个类的作用就是重写了 as_view() 方法, 让其增加一个字典参数(actions), 把请求方式名与接口函数名的对应的关系直接写在字典参数(actions)中, 源代码分析为:

class ViewSetMixin:
"""
This is the magic. Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource. For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions... view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
""" @classonlymethod
def as_view(cls, actions=None, **initkwargs):
....代码省略 # actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
....代码省略
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions # Bind methods to actions
# This is the bit that's different to a standard view
# 绑定请求方式method与接口函数action的对应关系
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
....代码省略
return self.dispatch(request, *args, **kwargs)
....代码省略
view.actions = actions
return csrf_exempt(view)

可以看到:

1. 在类的说明文档中, 就告诉了我们说这是重写的as_view()方法, 新增了一个actions参数用来绑定HTTP请求方式和对应的操作, 例如: MyViewSet.as_view({'get': 'list', 'post': 'create'})

2. 在as_view中的view()方法中, 同样通过反射进行上述的绑定

GenericViewSet/ViewSet

上述的ViewSetMixin类只是重写了as_view()方法让其新增一个actions参数而已, 而具体使用时还需要和前面提到的GenericAPIView或者更基础的APIView一起使用, 因为具体的处理逻辑还是需要走dispatch方法.

于是在viewsets.py中还提供了两个类, GenericViewSet(ViewSetMixin, generics.GenericAPIView) 和 ViewSet(ViewSetMixin, views.APIView) , 就像之前我们选择使用基础的APIView还是GenericAPIView一样, DRF也提供了两个对应的ViewSet类, 这里我们使用GenericViewSet实现简单的群查功能:

# 在urls.py中添加配置
from django.urls import path
from books import views urlpatterns = [
...
path('v4/', views.BookGenericViewSet.as_view({'get': 'list'})),
] # 在views.py中添加视图类
from rest_framework.viewsets import GenericViewSet
class BookGenericViewSet(GenericViewSet):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer def list(self, request):
books = self.get_queryset()
serializer = self.get_serializer(books, many=True)
return MyResponse(result=serializer.data)

ModelViewSet/ReadOnlyModelViewSet

我们在上一步继承了GenericViewSet, 但是list方法还是我们自己手动写的, 我们可以结合上面的mixin工具一起使用, 例如同时继承mixins的ListModelMixin和GenericViewSet实现群查, 这样list方法在mixin中就定义好了, 我们也不需要手动写list方法, 视图类代码就变得更加简单, 只需要定义类属性queryset和Serializer_class即可:

# 在urls.py中添加配置
from django.urls import path
from books import views urlpatterns = [
...
path('v5/', views.BookListMixinGenericViewSet.as_view({'get': 'list'})),
] # 在views.py中添加视图类
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet class BookListMixinGenericViewSet(mixins.ListModelMixin, GenericViewSet):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer

同理, 我们还可以加上其他接口的mixin工具, 如单查mixins.RetrieveModelMixin, 单增mixins.CreateModelMixin等等, 而这一步DRF也为我们想好了, 在viewsets中定义了一个 ModelViewSet 类, 它继承了mixin的所有接口类

class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass

同时, DRF还定义了一个 ReadOnlyModelViewSet 类, 顾名思义, 这个类只能用来查询, 只继承了 单查mixins.RetrieveModelMixin和 群查 mixins.ListModelMixin两个接口工具类

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `list()` and `retrieve()` actions.
"""
pass

我们可以按需使用ModelViewSet类还是ReadOnlyModelViewSet, 这里我们使用ModelViewSet, 不需要几行代码就能完成简单的单增, 单删, 单整体改, 单局部改, 单查, 群查6个接口, 但是这里需要注意的是由于使用的是mixin的默认接口函数, 我们之前自定义的响应类MyResponse就无法使用了, 除非我们再重写一下这些默认的接口函数:

# 在urls.py中添加配置
from django.urls import path
from books import views urlpatterns = [
...
path('v6/', views.BookModelViewSet.as_view({'get': 'list', 'post': 'create', })),
path('v6/<int:pk>/', views.BookModelViewSet.as_view(
{'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
] # 在views.py中添加视图类
from rest_framework import mixins
from rest_framework.viewsets import ModelViewSet class BookModelViewSet(ModelViewSet):
queryset = Book.objects.filter(is_delete=False)
serializer_class = BookSerializer

路由(Router)

有一些像Rails这样的Web框架提供自动生成Urls的功能。但是Django并没有。 REST framework为Django添加了这一功能,以一种简单、快速、一致的方式。

DRF的routers模块没有什么东西,主要包含下面几个类:

BaseRouter:路由的基类

SimpleRouter: 继承了BaseRouter,常用类之一

DefaultRouter:继承了SimpleRouter,常用类之一

APIRootView

我们主要使用的其实就是SimpleRouter和DefaultRouter, 基本用法为, 编辑urls.py:

from rest_framework.routers import DefaultRouter

# 1.创建路由对象
router = DefaultRouter()
# 2.注册视图类
router.register(r'v6', views.BookModelViewSet, basename='v6')
router.register(r'v5', views.BookListMixinGenericViewSet, basename='v5') urlpatterns = [
# path('v5/', views.BookListMixinGenericViewSet.as_view({'get': 'list'})),
# path('v6/', views.BookModelViewSet.as_view({'get': 'list', 'post': 'create', })),
# path('v6/<int:pk>/', views.BookModelViewSet.as_view(
# {'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})), # 3.将路由类的urls添加到urlpatterns中
path('', include(router.urls)),
]

django-rest-framework-源码解析003-视图家族和路由(APIView/GenericAPIView/mixins/generics/viewsets)的更多相关文章

  1. Django Rest Framework源码剖析(八)-----视图与路由

    一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...

  2. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

  3. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  4. django之admin源码解析

    解析admin的源码 第一步:项目启动,加载settings文件中的 INSTALLED_APPS 里边有几个app就加载几个,按照注册顺序来执行. 第二步:其中加载的是admin.py,加载每一个a ...

  5. springMVC源码解析--ViewResolverComposite视图解析器集合(二)

    上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...

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

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

  7. Django Rest Framework源码剖析(五)-----解析器

    一.简介 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数 ...

  8. Django Rest Framework源码剖析(七)-----分页

    一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...

  9. Django Rest Framework源码剖析(六)-----序列化(serializers)

    一.简介 django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似). ...

随机推荐

  1. SSM登录拦截验证

    /** * 登陆拦截器,用于后台管理系统拦截,判断用户是否登录 * @author ljy * @date 2015/8/19 */public class LoginForAdminIntercep ...

  2. Java 比较对象中的内容是否一致

    获取对象中的所有属性 private static Field[] getAllFields(Object object) { Class clazz = object.getClass(); Lis ...

  3. Nginx深入学习(一篇搞定)

     我们的口号是:人生不设限!  一.nginx简介 1.什么是nginx Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,特点是占有内存少,并发能力强,事实上nginx的并发 ...

  4. 【python + NATAPP】实现内网穿透的简易数据传输

    1. 服务端 接收两张图像的地址,返回这两张图像的相似度 import os, shutil, requests import cv2 import numpy as np import imgs_s ...

  5. LeetCode54. 螺旋矩阵

    题意是,输入一个二维数组,从数组左上角开始,沿着顺时针慢慢地"遍历"每一个元素且每一个元素只遍历一次, 在一个新的一维数组中记录遍历的顺序,最终的返回值就是这个数组. 思路:可以考 ...

  6. Unity ugui Anchor锚点自动适配画布中的相对位置

    本随笔参考了以下博客,在此基础上进行优化和改进: https://blog.csdn.net/qq_39640124/article/details/88284191 ugui中的Anchor预设如下 ...

  7. 《UNIX环境高级编程》(APUE) 笔记第三章 - 文件I/O

    3 - 文件I/O Github 地址 1. 文件描述符 对于内核而言,所有打开的文件都通过 文件描述符 (file descriptor) 引用.当打开一个现有文件或创建一个新文件时,内核向进程返回 ...

  8. 浅谈MySQL数据库

    目录 什么是数据库 定义 发展现状 数据库基本概念 数据库分类 关系数据库 非关系型数据库(NoSQL) 数据库启动与连接 启动服务端 连接数据库 用户信息查看 数据库的基本操作 表的基本操作 记录的 ...

  9. 部署JUnit

    JUnit的简介和使用:http://blog.csdn.net/luanlouis/article/details/37562165 jar包下载地址:http://www.java2s.com/C ...

  10. 为DLL文件添加强名称

    程序在编译时出现类似 "错误 1 程序集生成失败 -- 引用的程序集“XXXXXXXXXX”没有强名称" 这样的错误,是因为它不是强名称的,则需要进行以下操作: 例如:com.so ...