Python 学习笔记(十八)--Django REST Framework之GenericAPIView
1.基类
views.APIView 与 GenericsAPIView,后者继承前者。
class GenericAPIView(views.APIView): """ Base class for all other generic views. """
2.GenericAPIView的属性和方法
3. GenericAPIView 的属性queryset
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
在开发者编写的视图类中,可以直接为queryset 赋值。
例如:
queryset = XXX.objects.all() ---XXX代表某个model
serializer_class = XXXXX, --表示使用那个序列化类 来序列化 指定的数据。
4. GenericAPIView 的方法get_queryset
def get_queryset(self): """ Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using `self.queryset`. This method should always be used rather than accessing `self.queryset` directly, as `self.queryset` gets evaluated only once, and those results are cached for all subsequent requests. You may want to override this if you need to provide different querysets depending on the incoming request. (Eg. return a list of items that is specific to the user) """ assert self.queryset is not None, ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() return queryset
5.GenericAPIView 的方法get_serializer
对象集即多行数据时,需要传入参数many=True
def get_serializer(self, *args, **kwargs): """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() kwargs.setdefault('context', self.get_serializer_context()) return serializer_class(*args, **kwargs)
子方法get_serializer_class
def get_serializer_class(self): """ Return the class to use for the serializer. Defaults to using `self.serializer_class`. You may want to override this if you need to provide different serializations depending on the incoming request. (Eg. admins get full serialization, others get basic serialization) """ assert self.serializer_class is not None, ( "'%s' should either include a `serializer_class` attribute, " "or override the `get_serializer_class()` method." % self.__class__.__name__ ) return self.serializer_class
6.GenericAPIView 的方法get_object
def get_object(self): """ Returns the object the view is displaying. You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf. """ queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(queryset, **filter_kwargs) # May raise a permission denied self.check_object_permissions(self.request, obj) return obj
7.5个视图扩展类
from rest_framework.mixins importListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
作用
""" Basic building blocks for generic class based views. We don't bind behaviour to http method handlers yet, which allows mixin classes to be composed in interesting ways. """
8.GenericAPIView的视图子类
rest_framework的mixins 的子类 和 GenericAPIView 结合,创造了9个GenericAPIView的视图子类。
9.视图子类ListAPIView
class ListAPIView(mixins.ListModelMixin, GenericAPIView): """ Concrete view for listing a queryset. """ def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs)
10. 继承多个子类,方法大集合
from rest_framework.viewsets import ModelViewSet
源码
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
需要留意的是 ModelViewSet 继承的是GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): """ The GenericViewSet class does not provide any actions by default, but does include the base set of generic view behavior, such as the `get_object` and `get_queryset` methods. """ pass
还继承了ViewSetMixin
11. ViewSetMixin
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'}) """
需要特比注意的是,类重写了as_view方法,【当调用时,actions参数必须赋值,否则报错。例如as_view({'get':'list','post':'create'}) 】
actions 对应的参数为字典类型。
即路由中(url)需要配置对应关系,例如{'get':'list'},表示当get请求到来,就会执行list方法。
@classonlymethod def as_view(cls, actions=None, **initkwargs): ##传入的参数与APIView的都不一样 """ Because of the way class based views create a closure around the instantiated view, we need to totally reimplement `.as_view`, and slightly modify the view function that is created and returned. """ # The name and description initkwargs may be explicitly overridden for # certain route configurations. eg, names of extra actions. cls.name = None cls.description = None # The suffix initkwarg is reserved for displaying the viewset type. # This initkwarg should have no effect if the name is provided. # eg. 'List' or 'Instance'. cls.suffix = None # The detail initkwarg is reserved for introspecting the viewset type. cls.detail = None # Setting a basename allows a view to reverse its action urls. This # value is provided by the router through the initkwargs. cls.basename = None # 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'})`") # sanitize keyword arguments for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r" % ( cls.__name__, key)) # name and suffix are mutually exclusive if 'name' in initkwargs and 'suffix' in initkwargs: raise TypeError("%s() received both `name` and `suffix`, which are " "mutually exclusive arguments." % (cls.__name__)) def view(request, *args, **kwargs): self = cls(**initkwargs) if 'get' in actions and 'head' not in actions: actions['head'] = actions['get'] # 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 for method, action in actions.items(): handler = getattr(self, action) ##handler 就变成了action变量对应的内存地址了 setattr(self, method, handler) ##通过反射,handler 赋值给self.method ##经过上面的for循环,对象有了对象.get(对应的list)\对象.post(对应的create) ....这类方法。
self.request = request self.args = args self.kwargs = kwargs # And continue as usual return self.dispatch(request, *args, **kwargs) # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) # We need to set these on the view function, so that breadcrumb # generation can pick out these bits of information from a # resolved URL. view.cls = cls view.initkwargs = initkwargs view.actions = actions return csrf_exempt(view)
12 drf 默认配置文件查找顺序
(1)先从本视图类中查找;
(2)项目的setting配置文件中查找;
(3)drf默认的文件中查找(.../lib/site-packages/django/conf/global_settings.py)