摘要:代碼講解先貼一波源碼,的有很多,就講下最常見的。這是里面的東東這是里面的東東首先是為什么不需要寫判斷,而在子類里面定義即可,可以看到是下面的方法做了通用的處理。
關于閱讀代碼
我們知道,得益于python的語言特性,python的源碼是直接可以看的到的,而django是一種大而全的東東,雖然django的文檔看似全面,但實際上有些模塊寫的確實不怎么樣,而且平時遇到的需求也是多變的,有的時候你需要實現某種詭異的功能,當然我們可以用google或者做github的搬運工,但是有的時候框架已經給你做了很多事情,你只需要在多做那么一丟丟就能實現某種需求,這個時候看代碼比去扒github是快的多的。
先稍微扯一下Django generics view這玩意兒通常你百度或者google的時候,大部分時間會跟你講這是Class-based views。
第一次接觸的時候,我覺得他牛逼的地方在于只要在子類里面定義get post方法,就不需要寫類似這種東西了
if request.method.lower() == "get": do_something() else: do_otherthing()
其實與普通的method view不同的是,他的父類定義了一堆有用的方法,當你知道這些的時候你可以少些超多的代碼來做一些屌屌的事情。
來看下使用示例防止文件被刪除,貼幾段過來
class BaseMixin(object): def get_context_data(self,*args,**kwargs): context = super(BaseMixin,self).get_context_data(**kwargs) try: #熱門文章 context["hot_article_list"] = Article.objects.order_by("-view_times")[0:10] #導航條 context["nav_list"] = Nav.objects.filter(status=0) #最新評論 context["latest_comment_list"] = Comment.objects.order_by("-create_time")[0:10] except Exception as e: logger.error(u"[BaseMixin]加載基本信息出錯") return context class IndexView(BaseMixin,ListView): template_name = "blog/index.html" context_object_name = "article_list" paginate_by = PAGE_NUM #分頁--每頁的數目 def get_context_data(self,**kwargs): #輪播 kwargs["carousel_page_list"] = Carousel.objects.all() return super(IndexView,self).get_context_data(**kwargs) def get_queryset(self): article_list = Article.objects.filter(status=0) return article_list
這是項目原來的地址
如果你之前只寫過method的view,你可能會覺得很詭異,為毛這段代碼定義了幾個屬性,重寫了幾個方法就實現了我之前寫的那么一大坨長長的代碼?這種時候就必須看源碼了,比文檔來的直觀。
generic view代碼講解OK先貼一波源碼,django的generic view有很多,就講下最常見的list。
這是 base.py里面的東東
class View(object): """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = ["get", "post", "put", "delete", "head", "options", "trace"] def __init__(self, **kwargs): """ Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things. """ # Go through keyword arguments, and either save their values to our # instance, or raise an error. for key, value in kwargs.iteritems(): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ # sanitize keyword arguments for key in initkwargs: if key in cls.http_method_names: raise TypeError(u"You tried to pass in the %s method name as a " u"keyword argument to %s(). Don"t do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError(u"%s() received an invalid keyword %r" % ( cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, "get") and not hasattr(self, "head"): self.head = self.get 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=()) return view 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 self.request = request self.args = args self.kwargs = kwargs return handler(request, *args, **kwargs) def http_method_not_allowed(self, request, *args, **kwargs): allowed_methods = [m for m in self.http_method_names if hasattr(self, m)] logger.warning("Method Not Allowed (%s): %s", request.method, request.path, extra={ "status_code": 405, "request": self.request } ) return http.HttpResponseNotAllowed(allowed_methods)
這是list.py里面的東東
class MultipleObjectMixin(object): allow_empty = True queryset = None model = None paginate_by = None context_object_name = None paginator_class = Paginator def get_queryset(self): """ Get the list of items for this view. This must be an interable, and may be a queryset (in which qs-specific behavior will be enabled). """ if self.queryset is not None: queryset = self.queryset if hasattr(queryset, "_clone"): queryset = queryset._clone() elif self.model is not None: queryset = self.model._default_manager.all() else: raise ImproperlyConfigured(u""%s" must define "queryset" or "model"" % self.__class__.__name__) return queryset def paginate_queryset(self, queryset, page_size): """ Paginate the queryset, if needed. """ paginator = self.get_paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty()) page = self.kwargs.get("page") or self.request.GET.get("page") or 1 try: page_number = int(page) except ValueError: if page == "last": page_number = paginator.num_pages else: raise Http404(_(u"Page is not "last", nor can it be converted to an int.")) try: page = paginator.page(page_number) return (paginator, page, page.object_list, page.has_other_pages()) except InvalidPage: raise Http404(_(u"Invalid page (%(page_number)s)") % { "page_number": page_number }) def get_paginate_by(self, queryset): """ Get the number of items to paginate by, or ``None`` for no pagination. """ return self.paginate_by def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True): """ Return an instance of the paginator for this view. """ return self.paginator_class(queryset, per_page, orphans=orphans, allow_empty_first_page=allow_empty_first_page) def get_allow_empty(self): """ Returns ``True`` if the view should display empty lists, and ``False`` if a 404 should be raised instead. """ return self.allow_empty 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 smart_str("%s_list" % object_list.model._meta.object_name.lower()) else: return None def get_context_data(self, **kwargs): """ Get the context for this view. """ queryset = kwargs.pop("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 } context.update(kwargs) if context_object_name is not None: context[context_object_name] = queryset return context class BaseListView(MultipleObjectMixin, View): def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty and len(self.object_list) == 0: raise Http404(_(u"Empty list and "%(class_name)s.allow_empty" is False.") % {"class_name": self.__class__.__name__}) context = self.get_context_data(object_list=self.object_list) return self.render_to_response(context)
首先是為什么不需要寫if else判斷,而在子類里面定義get post即可,可以看到是下面的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 self.request = request self.args = args self.kwargs = kwargs return handler(request, *args, **kwargs)
而通用的listview實際上就是一個父類實現了get方法,其中的MultipleObjectMixin實現了超多的方法供你重寫,基本上大部分時間我們重寫MultipleObjectMixin里面的對應方法以及設置合適的屬性就可以完成大部分的業務邏輯。
BaseListView首先通過 self.get_queryset() 方法拿到了你需要list的對象,中間一坨是分頁的東東,下面的context則是最后需要render_to_response時傳的參數,這樣看就跟以前自己通過method view寫的東西一一對應了。而我們只需要根據不同的業務邏輯實現里面的覆蓋實現里面的方法即可,有些連覆蓋都不需要是要在設置合適的屬性,到這里我就不需要根據具體的方法具體講解了,大家自己看。
class BaseListView(MultipleObjectMixin, View): def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty and len(self.object_list) == 0: raise Http404(_(u"Empty list and "%(class_name)s.allow_empty" is False.") % {"class_name": self.__class__.__name__}) context = self.get_context_data(object_list=self.object_list) return self.render_to_response(context)一個現實中的例子
上面僅僅是如何使用django默認的各種generic view,但是有時候默認的機制并不能實現你的需求,例如現在在移動端的大潮下,html配飾移動端一般有兩種方案:1.響應式;2.兩套頁面完全分開。兩種方案各有利弊,如果我們需要根據根據user agent來做不同終端的頁面渲染,甚至是不同的邏輯(web和移動端邏輯不同這很正常),這種時候很容易想到重寫dispatch方法,來做到一種通用的處理方式。
from django.views.generic import View as DjangoView class View(DjangoView): def _get_handler(self, request): """ 根據ua獲取handler """ handler_name = request.method.lower() if handler_name in self.http_method_names: handler = getattr(self, handler_name) if not handler: return self.http_method_not_allowed user_agent = request.META.get("HTTP_USER_AGENT", "").lower() handler = getattr(self, handler_name, self.http_method_not_allowed) user_agents = ["ipad", "iphone", "ipod", "androidtv", "android"] for ua in user_agents: if ua in user_agent: handler_name = "{}_{}".format(handler_name, ua) break return getattr(self, handler_name) or handler 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. # 支持user agent跳轉 # 如果實現了對應的方法,則直接使用對應ua的規則 # 例如method=get, ua為iphone的ua,子類實現 get_iphone, 則使用get_iphone進行render handler = self._get_handler(request) self.request = request self.args = args self.kwargs = kwargs return handler(request, *args, **kwargs)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/37619.html
摘要:有編程基礎的同學都知道,類是面向對象技術中非常重要的概念。有區別的是,是作為功能添加到子類中的,而不是作為父類。首先調用父類方法,將這個對象賦值給變量,然后再對其進行統計瀏覽量的操作,最后將對象返回。 說是完結,馬上又開始寫進階篇了。 本章不會為博客項目增加新功能,但是也同樣重要,因為我們要學習高逼格的基于類的視圖。 什么是類視圖 前面章節中寫的所有視圖都是基于函數的,即def;而類視...
摘要:官網地址個人學習筆記四我們也能夠使用來編寫我們的,而不是基于正如我們所看到的,這是一個強大的模式,它使我們的功能能夠復用,并保持我們的代碼整潔。檢索,更新或者刪除一個實例。接下來將展示使用來組成我們的。 (官網地址)[django rest framework個人學習筆記(四)————Tutorial] Class-based views 我們也能夠使用class-based view...
摘要:官網地址個人學習筆記四我們也能夠使用來編寫我們的,而不是基于正如我們所看到的,這是一個強大的模式,它使我們的功能能夠復用,并保持我們的代碼整潔。檢索,更新或者刪除一個實例。接下來將展示使用來組成我們的。 (官網地址)[django rest framework個人學習筆記(四)————Tutorial] Class-based views 我們也能夠使用class-based view...
摘要:官網地址個人學習筆記四我們也能夠使用來編寫我們的,而不是基于正如我們所看到的,這是一個強大的模式,它使我們的功能能夠復用,并保持我們的代碼整潔。檢索,更新或者刪除一個實例。接下來將展示使用來組成我們的。 (官網地址)[django rest framework個人學習筆記(四)————Tutorial] Class-based views 我們也能夠使用class-based view...
摘要:看起來不錯再次,它現在仍然非常類似于基于功能的視圖。我們還需要重構一下我們使用基于類的視圖。中文文檔目錄中文教程序列化中文教程請求和響應中文教程基于類的視圖中文教程驗證和權限中文教程關系和超鏈接中文教程中文教程模式和客戶端庫 我們也可以使用基于類的視圖編寫我們的API視圖,而不是基于函數的視圖。我們將看到這是一個強大的模式,允許我們重用常用功能,并幫助我們保持代碼DRY。 使用基于類的...
閱讀 2792·2021-11-04 16:15
閱讀 3469·2021-09-29 09:35
閱讀 4058·2021-09-22 15:45
閱讀 1422·2019-08-30 15:55
閱讀 1695·2019-08-30 15:44
閱讀 2725·2019-08-29 12:56
閱讀 2703·2019-08-26 13:30
閱讀 2179·2019-08-23 17:00