Django 中常用的第三方插件庫介紹
今天我們會介紹在 Django 生態(tài)中比較火的一些第三方插件。正是這些插件,讓我們開發(fā)網站變得如此簡單。同時我會就一個熱門的第三方框架談一談如何學習該框架的源碼。接下來,讓我們一起開始今天的學習吧。
1. Django 中常用的插件庫
1.1 Django Rest Framework
Django Rest Framework 是一個強大且靈活的工具包,用以快速構建 Web API。為啥要使用它來構建 Web API呢?除了 DRF 可以在 Django 的基礎上迅速實現(xiàn) API 外,它自身還帶有 WEB 的測試頁面,可以方便的測試自己的 API。這點非常類似于 Java Web 開發(fā)中的 Swagger 插件,對于我們測試自己的 API 接口時非常有幫助的。
我們來總結 DRF 框架具備的一些特點:
- 提供了定義序列化器 Serializer 的方法,可以快速根據(jù) Django ORM 或者其他庫自動序列化/反序列化;
- 提供了豐富的類視圖 和 Mixin 擴展類,可以進一步簡化視圖的編寫;
- 多種身份認證和權限認證方式的支持;
- 內置了限流系統(tǒng);
- 直觀的 API Web界面;
- 可擴展性 , 插件豐富;
- 完備的文檔以及良好的社區(qū)支持
- 諸多大廠(如 Mozilla, Red Hat 等)使用和點贊;
最后作為一個開源框架,查看下其項目 Github 地址時十分必要的。一個原則是:stars 越多,項目越火,可以放心使用。
項目的地址和文檔如下:
可以看到,無論是從 star 數(shù)還是 contributor 人數(shù)上看, DRF 都可以稱為是熱門框架了。大部分做 Django 開發(fā)的都會使用 DRF 這個插件,用于開發(fā)高質量的 Web API,這個框架也是我們后面學習和研究的重點。
1.2 Django Celery
首先介紹下 Celery 模塊,Celery 是一款非常簡單、靈活、可靠的分布式系統(tǒng),可用于處理大量消息,并且提供了一整套操作此系統(tǒng)的一系列工具。另外,Celery 是一款消息隊列工具,可用于處理實時數(shù)據(jù)以及任務調度。官網給出了 Celery 如下四個特點:
- 簡單:開箱機用,維護簡單,不需要使用配置文件;
- 高可靠性:如果連接丟失或者出現(xiàn)故障,客戶端進程會自動重試。一些 broker 會以主/主或者主/備的方式維持系統(tǒng)的高可靠性;
- 快速:單個 Celery 進程每分鐘內可以處理數(shù)百萬個任務,往返的延遲在毫秒級別(使用RabbitMQ 做中間件,再加上一些優(yōu)化的設置);
- 靈活:幾乎 Celery 的每個部分都可以自行擴展,如使用自定義池、序列化器,壓縮方案、日志記錄,調度程序,消費者,生產者,代理傳輸?shù)鹊取?/li>
注意到基于 Django 框架構建的 Web 系統(tǒng)實際上是一個同步服務。這意味著當客戶端發(fā)起一個請求時,后端只有在視圖函數(shù)處理完后才會返回結果。如果這個請求背后要做的工作比較耗時,或者因為某種原因導致非常耗時,那么此時客戶端會一直等待請求的響應,這非常影響用戶體驗。對于一個優(yōu)秀的網站而言,良好的用戶體驗十分重要,這也說明了一個支持異步功能的第三方插件的重要性。為了能讓 Django 搭建的 Web 系統(tǒng)支持這樣的異步功能,于是 django-celery 便應運而生。
django-celery項目之后也被移到 celery 下進行統(tǒng)一管理。它相比原 celery 項目在 star 數(shù)和貢獻者數(shù)上要遜色不少。
可以看到,Celery 模塊火熱程度可以媲美 DRF。而它不僅僅用在 Django 項目中,其它框架甚至個人項目中也常常使用 Celery 來完成異步場景的需求。
1.3 Django Guardian
上一節(jié)中我們介紹了 Django 的權限管理, 知道 Django 僅僅提供的是一種全局權限。這種簡單的全局權限控制機制在很多場景下并不適用,因此需要引入另一種更細的權限機制:對象權限 (object permission)。所謂的 Object Permission 其實是一種對象顆粒度上的權限機制,它允許為每個具體對象授權 ,在 Django 中其實已經包含了 object permission 的模塊,但沒有具體實現(xiàn),必須要使用第三方的插件完成相應的功能。django-guardian 是目前比較活躍的一個 django extension,提供了一種有效的 object permission 控制機制,與 django 原生機制一脈相承,而且能快速整合到 django-admin 中,十分推薦使用。
接下來繼續(xù)看下它在 Github 上的表現(xiàn):
django-guardian 項目的地址和文檔如下:
從更新的頻率上看,django-guardian 項目還是非?;钴S的,更新速度可能沒有 Django 這樣的大型框架頻繁,但是還是保持著一定的迭代速度,是比較值得使用并花心思研究的。
1.4 Django Xadmin
Django Xadmin 是國內程序員 sshwsfc 基于 Django 打造的一款簡單易用而且又好看的后臺管理系統(tǒng)。官網宣傳語便是:打造管理系統(tǒng)從未如此簡單。其宣稱的幾個核心特點如下:
- 基于 Bootstrap3,適合多種屏幕顯示;
- 內置功能豐富,除了基本的 CURD 功能外,還有豐富的插件功能,如數(shù)據(jù)的導出、書簽、圖表、圖片相冊等多種擴展功能;
- 強大的插件系統(tǒng),通過制作 Xadmin 插件可以擴展系統(tǒng)的任何一個功能點;
- 完善的權限系統(tǒng),可配置、可定制,安全可靠;
來看一看它的 Github 地址, 主要關注 star 和 contributor 人數(shù)??梢钥吹竭@個項目的火熱程度和 DRF 項目相比還有點距離,在國產框架中已經算很不錯的了。但是截止到2020年5月4日,看到的上次 commit 記錄好像還是2019年4月份,項目似乎是停滯更新了。如果是生產環(huán)境的話,需要慎重考慮,但是作為學習使用,還是十分推薦的。
2. 如何深入插件源碼學習?
我們以 DRF 框架為例,聊一聊如何深入 DRF 框架的源碼學習。首先肯定是下載穩(wěn)定版本為 DRF 源碼到本地,這是為了方便自己閱讀代碼。截止到2020年5月10日,DRF 的 Github 官方地址發(fā)布的最新版本為3.11.0,我們會用該版本的代碼來進行相關的演示和說明。以下是 DRF-3.11.0 源代碼截圖,里面的代碼量還是比較大的,不過相對于 Django 的代碼而言就會少很多,我們前面能學習并跟蹤 Django 框架的源碼,拿下 DRF 源碼自然也不在話下。
一般而言,推薦學習一個 Django 第三方插件源碼的過程如下:
- 第一步:熟練使用 Django 框架以及熟悉 Django 框架源碼。所有的 Django 第三方插件代碼里會大量調用 Django 源碼的類或者方法,并在其基礎上進行擴展或者進一步創(chuàng)新。以我們必須先掌握 Django 的源碼,才能繼續(xù)學習 DRF 的源碼;
- 第二步:仔細閱讀官方文檔手冊進行學習,掌握框架的基本用法;
- 第三步:通過官方文檔,實戰(zhàn) DRF 框架;每次在用熟練 DRF 提供的類或者方法后,就可以對應地查看源碼,并分析 DRF 背后所做的工作。每掌握一個模塊的基本用法,就可以深入學習對應模塊的源碼,同時在源碼中我們還可以發(fā)現(xiàn)該模塊中的更多用法,然后再次實踐,以加深對源碼的理解。
我們按照上面的過程來簡單走一遍。首先我們前面對 Django 的幾大模塊的源碼都有涉獵,算是滿足了第一步要求。接下來我們用官方給的快速入門教程完成我們的第一次 Django REST framework 框架的初體驗。
-
模型序列化器:給會員表 member 添加一個序列化器類,放到新建的 serializers.py 文件中。
from rest_framework import serializers from hello_app.models import Member class MemberSerializer(serializers.ModelSerializer): class Meta: model = Member fields = ("id", "name", "age", "sex", "occupation", "phone_num", "email", "city", "vip_level_id")
-
準備 View 視圖:添加一個對會員表操作的視圖類,我們用最簡單的形式即可。
# 代碼位置:hello_app/views.py # ... from rest_framework import viewsets from rest_framework import permissions # ... class MemberViewSet(viewsets.ModelViewSet): # 設置queryset queryset = Member.objects.all().order_by('-register_date') # 設置序列化器 serializer_class = MemberSerializer # 設置認證器 permission_classes = [permissions.IsAuthenticated]
-
編寫 URLConf 配置:Django REST framework 框架改良了 URLConf 配置的寫法,后面會研究這種寫法,先直接使用官方的示例即可。
# 代碼位置:hello_app/urls.py # ... from rest_framework import routers router = routers.DefaultRouter() router.register(r'members', views.MemberViewSet) urlpatterns = [ # ... path('', include(router.urls)) ]
另外,由于我們對
MemberViewSet
視圖加上了認證,所以必須要在入口的 urls.py 中上如下的 URLConf 的配置。# 代碼位置: first_django_app/urls.py # ... urlpatterns = [ # ... path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
注意:不添加和添加這行 URLConf 配置的效果圖如下所示。
-
接下來,最后一步是設置視圖的相關配置以及注冊 rest_framework 應用。
# 代碼位置:first_django_app/settings.py # ... INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # 注冊第三方應用 'rest_framework', # 注冊應用 'hello_app' ] REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 5 } # ...
-
最后我們啟動服務,來一起看看效果。我們之前創(chuàng)建過一個超級用戶admin/admin.1234!,接下來會用這個通過 DRF 的認證。
從上面的演示中,我們看到了 Django REST framework 框架給我們做的接口測試頁面,我們只需要簡單繼承下MemberViewSet
即可,然后添加相關屬性即可立即擁有這樣一個完整的接口測試頁面。后臺服務主要提供接口數(shù)據(jù),我們也可以使用 curl 命令來獲取和操作相應的模型表。
[root@server ~]# curl -H 'Accept: application/json; indent=4' -u admin:admin.1234! http://127.0.0.1:8888/hello/members/?page=3
{
"count": 103,
"next": "http://127.0.0.1:8888/hello/members/?page=4",
"previous": "http://127.0.0.1:8888/hello/members/?page=2",
"results": [
{
"id": 9,
"name": "spyinx-5",
"age": "39",
"sex": 0,
"occupation": "product",
"phone_num": "18015702646",
"email": "225@qq.com",
"city": "shanghai",
"vip_level_id": null
},
{
"id": 10,
"name": "spyinx-6",
"age": "26",
"sex": 0,
"occupation": "ops",
"phone_num": "18790082215",
"email": "226@qq.com",
"city": "beijing",
"vip_level_id": null
},
{
"id": 11,
"name": "spyinx-7",
"age": "23",
"sex": 0,
"occupation": "security",
"phone_num": "18354491889",
"email": "227@qq.com",
"city": "guangzhou",
"vip_level_id": null
},
{
"id": 12,
"name": "spyinx-8",
"age": "26",
"sex": 1,
"occupation": "ui",
"phone_num": "18406891676",
"email": "228@qq.com",
"city": "wuhan",
"vip_level_id": null
},
{
"id": 13,
"name": "spyinx-9",
"age": "26",
"sex": 0,
"occupation": "ops",
"phone_num": "18036496230",
"email": "229@qq.com",
"city": "wuhan",
"vip_level_id": null
}
]
}
在上面這個過程走通之后,我們可以看到其實這個例子中已經涉及到了 DRF 中的許多類,比如用于序列化的類ModelSerializer
、視圖類 ModelViewSet
、分頁類 PageNumberPagination
等等。從這個案例中,我們可以找到許多學習 DRF 源碼的切入點。首先看用到的視圖類 ModelViewSet
:
# 源碼位置:rest_framework/viewsets.py
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
通過學習 Django 的視圖,我們了解了 Mixin 這個概念,所以容易理解這里的代碼,視圖繼承 GenericViewSet
,同時也繼承了數(shù)個 Mixin。這些 Mixin 從命名上就很容易知道其功能用法。進一步翻看其實現(xiàn)類,也能發(fā)現(xiàn)其具體含義 。以 mixins.CreateModelMixin
類為例:
# rest_framework/mixins.py
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
CreateModelMixin
的主要功能就是提供了 create()
方法,讓視圖擁有新增記錄的功能。其他的 Mixin 會提供類似的函數(shù),讓視圖具有某一特定的功能。接下來我們的重點放到 GenericViewSet
類的學習上。
# 源碼位置:rest_framework/viewsets.py
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
類,另一個 generics.GenericAPIView
類。先追后面的 View 類,實現(xiàn)代碼如下:
從這里我們看到了一些熟悉的屬性,如 queryset
,serializer_class
以及用于分頁的 pagination_class
。這個繼承的 APIView
類同樣也是 Django REST framework 框架自己定義的類,我們繼續(xù)追進 APIView
類的實現(xiàn)代碼:
最后 APIView
這個類繼承的 View
正是 Django 中我們學過的 View 視圖類。
from django.views.generic import View
那這樣子,我們也算清楚了一些事情。Django REST framework 框架中定義的視圖是在 Django 的 View 視圖類上封裝和改進來的?,F(xiàn)在一個疑問就來了,看我們前面使用 Django 的視圖中,URLConf 配置如下:
urlpatterns = [
path('test-cbv/', views.TestView.as_view(), name="test-cbv"),
]
我們也分析過對應的 View 類以及 as_view()
方法,它將 GET 請求映射到視圖類的 get()
方法,POST 請求則映射到 post()
方法;然而我們這里一路走下來并有沒有看到對應的 get()
或者 post()
方法。但是視圖類繼承的多個 Mixin 中提供了 create()
、list()
等這樣的方法,那么他們是如何和 URLConf 配置對應上的呢?我們現(xiàn)在要通過代碼去找出前面配置 URLConf 代碼的內部原理:
from django.conf.urls import include
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'members', views.MemberViewSet)
urlpatterns = [
# ...
path('', include(router.urls))
]
來看看上面的 URLConf 配置。這個時候,我們需要去看 Django REST Framework 中的 DefaultRouter
類,包括注冊方法 register
() 以及 urls
屬性值的獲取。最后還要看 Django 中的 include() 方法的代碼,才能理清楚 URL 和視圖的映射關系。
先追蹤 Django REST Framework 中的 DefaultRouter
類實現(xiàn),該類繼承自 SimpleRouter
,SimpleRouter
又繼承自 BaseRouter
。為了加快速度,我們直接定位到基類 BaseRouter,可以看到 register()
方法和 urls
屬性的定義,如下:
# 源碼位置:rest_framework/routers.py
class BaseRouter:
def __init__(self):
self.registry = []
def register(self, prefix, viewset, basename=None):
if basename is None:
basename = self.get_default_basename(viewset)
self.registry.append((prefix, viewset, basename))
# invalidate the urls cache
if hasattr(self, '_urls'):
del self._urls
def get_default_basename(self, viewset):
"""
If `basename` is not specified, attempt to automatically determine
it from the viewset.
"""
raise NotImplementedError('get_default_basename must be overridden')
def get_urls(self):
"""
Return a list of URL patterns, given the registered viewsets.
"""
raise NotImplementedError('get_urls must be overridden')
@property
def urls(self):
if not hasattr(self, '_urls'):
self._urls = self.get_urls()
return self._urls
可以看到,在執(zhí)行 router.register(r'members', views.MemberViewSet)
后其實等同于給 registry
數(shù)組添加一個元組元素,用于存儲映射關系。而 urls
屬性值則是調用 get_urls()
方法得到的。
class DefaultRouter(SimpleRouter):
"""
The default router extends the SimpleRouter, but also adds in a default
API root view, and adds format suffix patterns to the URLs.
"""
# ...
def get_urls(self):
"""
Generate the list of URL patterns, including a default root view
for the API, and appending `.json` style format suffixes.
"""
urls = super().get_urls()
if self.include_root_view:
view = self.get_api_root_view(api_urls=urls)
root_url = url(r'^$', view, name=self.root_view_name)
urls.append(root_url)
if self.include_format_suffixes:
urls = format_suffix_patterns(urls)
return urls
可以看到它先是調用了父類的 get_urls()
方法,另外又添加了一些映射規(guī)則。我們添加如下一行 print()
語句:
class DefaultRouter(SimpleRouter):
def get_urls(self):
"""
Generate the list of URL patterns, including a default root view
for the API, and appending `.json` style format suffixes.
"""
urls = super().get_urls()
print('父類調用得到的urls={}'.format(urls))
# ...
然后啟動服務,可以看到如下的結果:
(django-manual) [root@server first_django_app]# python manage.py runserver 0:8888
Watching for file changes with StatReloader
Performing system checks...
父類調用得到的urls=[<URLPattern '^members/$' [name='member-list']>, <URLPattern '^members/(?P<pk>[^/.]+)/$' [name='member-detail']>]
System check identified no issues (0 silenced).
May 15, 2020 - 13:30:04
Django version 2.2.12, using settings 'first_django_app.settings'
Starting development server at http://0:8888/
Quit the server with CONTROL-C
可以看到,這個 ^members/$
的URL 配置是由父類的 get_urls()
方法得到的。在父類 SimpleRouter
中的get_urls()
方法中,我已經做好了相關的注釋,最關鍵的代碼就在最后的 append()
部分,那里添加的便是最后 URL 和 視圖函數(shù)的關系。
class SimpleRouter(BaseRouter):
# ...
def get_urls(self):
# ...
# 前面介紹過這個 registry 屬性,就是通過 register() 方法得到的
for prefix, viewset, basename in self.registry:
# ...
for route in routes:
# Only actions which actually exist on the viewset will be bound
mapping = self.get_method_map(viewset, route.mapping)
if not mapping:
continue
# Build the url pattern
regex = route.url.format(
prefix=prefix,
lookup=lookup,
# 尾部加上"/"
trailing_slash=self.trailing_slash
)
# 處理一些簡單情況
if not prefix and regex[:2] == '^/':
regex = '^' + regex[2:]
initkwargs = route.initkwargs.copy()
initkwargs.update({
'basename': basename,
'detail': route.detail,
})
# 最最核心的部分代碼,這里得到視圖函數(shù)
view = viewset.as_view(mapping, **initkwargs)
# 視圖名稱
name = route.name.format(basename=basename)
# 添加映射規(guī)則
ret.append(url(regex, view, name=name))
return ret
我們可以看到最后添加的映射規(guī)則就是這一句:ret.append(url(regex, view, name=name))
,我們繼續(xù)看看這個 url()
方法,它調用的正是 Django 中的 url()
方法,內容如下:
# 源碼路徑:django/conf/urls.py
# ...
def url(regex, view, kwargs=None, name=None):
return re_path(regex, view, kwargs, name)
這個 url()
方法和我們之前在 Django 中用 repath()
以及 path()
差不多一致的。第一個參數(shù)是 url 規(guī)則,第二個便是視圖函數(shù)。比較重要的就是這里得到 view
的函數(shù)了,它便是真正的視圖函數(shù)。它和前面 Django 中的一樣,通過 as_view()
得到的。那么這個 as_view()
方法在哪呢,通過父類追蹤,可知 Django 的父類中本身就有 as_view()
方法,但是在前一個繼承的Mixin 中重寫了該方法,因此調用的便是該 Mixin 中的 as_view()
方法:
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):
"""
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.
"""
# ...
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
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
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)
和 Django 中的一樣,這里最后的 as_view()
方法最后返回的便是視圖函數(shù)。那么對應的 /hello/members/
請求進來后,有 view()
方法進行處理,最后調用的和 Django 中的一樣:
return self.dispatch(request, *args, **kwargs)
我們去 Django 中看這個 dispatch()
方法的源碼:
# 源碼位置:django/views/generic/base.py
class View:
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
# ...
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)
那么執(zhí)行 /hello/members/
請求到這里是,handler 是哪個?我們繼續(xù)翻看前面的 Mixin 類,有這樣一段代碼:
class ViewSetMixin:
# ...
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
# ...
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
# ...
# ...
# ...
這里就非常明顯了,我們大概也能猜到一些。就是設置 (get|post|put|delete) 請求對應的方法,比較好的方式時我們在這里打印下請求,并在前端進行下請求測試,看看這里到底設置了啥?
class ViewSetMixin:
# ...
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
# ...
for method, action in actions.items():
print('請求處理view視圖函數(shù):method={}, action={}'.format(method, action))
handler = getattr(self, action)
setattr(self, method, handler)
# ...
# ...
# ...
我們啟動服務請求以下路徑 /hello/members/
,可以得到如下輸出結果:
(django-manual) [root@server first_django_app]# python manage.py runserver 0:8888
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
May 15, 2020 - 17:07:38
Django version 2.2.12, using settings 'first_django_app.settings'
Starting development server at http://0:8888/
Quit the server with CONTROL-C.
請求處理view視圖函數(shù):method=get, action=list
請求處理view視圖函數(shù):method=post, action=create
[15/May/2020 17:07:43] "GET /hello/members/ HTTP/1.1" 200 14426
結合 Django 中的 dispatch()
方法,我們終于知道了 get 請求最后會調用視圖類中的 list()
方法去處理,而這個 list()
方法正是 ListModelMixin 中的。另外 post 請求則對應著視圖類中的 create()
方法,而這個屬性則來自 CreateModelMixin。這樣我們總算理解了前面的 URLConf 的映射流程以及對應的真正視圖處理函數(shù)。
帶著問題去追源碼是我比較推薦的一個學習方式。完成一個模塊的學習就要去思考,去追蹤這個案例背后的執(zhí)行過程,這樣才能更好的掌握這個模塊。今天的分享就到此結束了,DRF 中還有很多代碼等著你們去探索,去實踐,祝大家學習愉快!
3. 小結
本小節(jié)中,我們介紹了多種流行的基于 Django 的第三方插件,好用又好玩,而且它們的組合能幫我們迅速開發(fā)一個完整而又美妙的網站。當然,流行的第三方插件遠不止這些,我們在掌握好一定的開發(fā)基礎后,也可以自行制作第三方插件。然而長路漫漫,還需要靜下心來仔細學習和研究代碼,才有可能在未來成為別人眼中的大神。