使用通用视图后,Django请求处理过程(以ListView为例):
在我们自定义的视图中:
class IndexView(ListView): template_name = 'blog/index.html' context_object_name = 'article_list' def get_context_data(self, **kwargs): kwargs['category_list'] = Category.objects.all() return super(IndexView, self).get_context_data() def get_queryset(self): return Article.objects.filter(status=1)
ListView的定义:
class ListView(MultipleObjectTemplateResponseMixin, BaseListView): """ Render some list of objects, set by `self.model` or `self.queryset`. `self.queryset` can actually be any iterable of items, not just a queryset. """
View中as_view方法:
class View(object): @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ 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. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs)
dispatch方法:
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
dispatch方法中根据http请求的method得到handler的值(get,post...),接着调用handler方法,如果是get请求,则调用的是get方法。get方法定义在BaseListView中:
class BaseListView(MultipleObjectMixin, View): """ A base view for displaying a list of objects. """ def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty: # When pagination is enabled and object_list is a queryset, # it's better to do a cheap query than to load the unpaginated # queryset in memory. if (self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists')): is_empty = not self.object_list.exists() else: is_empty = len(self.object_list) == 0 if is_empty: raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {'class_name': self.__class__.__name__}) context = self.get_context_data() return self.render_to_response(context)
get_queryset、get_context_data在父类中的定义:
class MultipleObjectMixin(ContextMixin): """ A mixin for views manipulating multiple objects. """ allow_empty = True queryset = None model = None paginate_by = None paginate_orphans = 0 context_object_name = None paginator_class = Paginator page_kwarg = 'page' ordering = None def get_queryset(self): """ Return the list of items for this view. The return value must be an iterable and may be an instance of `QuerySet` in which case `QuerySet` specific behavior will be enabled. """ if self.queryset is not None: queryset = self.queryset if isinstance(queryset, QuerySet): queryset = queryset.all() elif self.model is not None: queryset = self.model._default_manager.all() else: raise ImproperlyConfigured( "%(cls)s is missing a QuerySet. Define " "%(cls)s.model, %(cls)s.queryset, or override " "%(cls)s.get_queryset()." % { 'cls': self.__class__.__name__ } ) ordering = self.get_ordering() if ordering: if isinstance(ordering, six.string_types): ordering = (ordering,) queryset = queryset.order_by(*ordering) return queryset def get_context_object_name(self, object_list): """ Get the name of the item to be used in the context. """ if self.context_object_name: return self.context_object_name elif hasattr(object_list, 'model'): return '%s_list' % object_list.model._meta.model_name else: return None def get_context_data(self, **kwargs): """ Get the context for this view. """ queryset = kwargs.pop('object_list', self.object_list) page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size) context = { 'paginator': paginator, 'page_obj': page, 'is_paginated': is_paginated, 'object_list': queryset } else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super(MultipleObjectMixin, self).get_context_data(**context)
当我们自定义的视图函数IndexView中重写get_context_data时,可将需传递到template的数据赋值给kwargs变量,最终数据会加入到context上下文中:
if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs)
最后回到BaseListView类的get方法,调用了self.render_to_response(context)进行响应输出。