Django--admin源碼流程


 

admin.py

 
from django.contrib import admin
from . import models
"""
通過原生的django admin來創造數據
"""
admin.site.register(models.User)
admin.site.register(models.Role)
admin.site.register(models.Permission)
admin.site.register(models.Group)
admin.site.register(models.Menu)
 

url.py

 
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
]
 

以上兩個文件都引用的是django.contrib.admin.site中的方法,site是AdminSite實例化出來的一個對象,所以site調用的register方法和urls屬性(方法被@property裝飾器裝飾)是AdminSite這個類中定義的

django啟動的時候,順序為:先register,然后在urls分配

先看admin.site.register干了什么

在源碼AdminSite這個類的構造方法初始化定義了一個空字典 _registry={}

?
1
2
3
4
5
6
7
8
class AdminSite( object ):
     ...
     def __init__( self , name = 'admin' ):
         self ._registry = {}  # model_class class -> admin_class instance
         self .name = name
         self ._actions = { 'delete_selected' : actions.delete_selected}
         self ._global_actions = self ._actions.copy()
         all_sites.add( self )

看源碼中的admin.site.register函數如下

 
    def register(self, model_or_iterable, admin_class=None, **options):
        """
        Registers the given model(s) with the given admin class.

        The model(s) should be Model classes, not instances.

        If an admin class isn't given, it will use ModelAdmin (the default
        admin options). If keyword arguments are given -- e.g., list_display --
        they'll be applied as options to the admin class.

        If a model is already registered, this will raise AlreadyRegistered.

        If a model is abstract, this will raise ImproperlyConfigured.
        """
        if not admin_class:
            admin_class = ModelAdmin

        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model._meta.abstract:
                raise ImproperlyConfigured(
                    'The model %s is abstract, so it cannot be registered with admin.' % model.__name__
                )

            if model in self._registry:
                raise AlreadyRegistered('The model %s is already registered' % model.__name__)

            # Ignore the registration if the model has been
            # swapped out.
            if not model._meta.swapped:
                # If we got **options then dynamically construct a subclass of
                # admin_class with those **options.
                if options:
                    # For reasons I don't quite understand, without a __module__
                    # the created class appears to "live" in the wrong place,
                    # which causes issues later on.
                    options['__module__'] = __name__
                    admin_class = type("%sAdmin" % model.__name__, (admin_class,), options)

                # Instantiate the admin class to save in the registry
                self._registry[model] = admin_class(model, self)
 

register函數第一個參數是注冊的模塊名稱,第二個參數不傳默認是None,但是實際使用的是ModelAdmin,ModelAdmin已模塊名稱作為參數實例化得到的對象作為 registry字典的value。簡化如下:

 
def register(self, model_or_iterable, admin_class=None, **options):
    if not admin_class:
        admin_class = ModelAdmin

    for model in model_or_iterable:
        .......
        .......
        self._registry[model] = admin_class(model, self)
 

再看admin.site.urls干了什么

源碼精簡如下:

 
def get_urls(self):
    from django.conf.urls import url, include
    from django.contrib.contenttypes import views as contenttype_views

    def wrap(view, cacheable=False):
        ......
        return update_wrapper(wrapper, view)

    # Admin-site-wide views.
    urlpatterns = [
        url(r'^$', wrap(self.index), name='index'),
        url(r'^login/$', self.login, name='login'),
        url(r'^logout/$', wrap(self.logout), name='logout'),
        url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'),
        ......
    ]

    valid_app_labels = []
    for model, model_admin in self._registry.items():
        urlpatterns += [
            url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
        ]
        if model._meta.app_label not in valid_app_labels:
            valid_app_labels.append(model._meta.app_label)
        ......
        
    return urlpatterns

@property
def urls(self):
    return self.get_urls(), 'admin', self.name #返回的是一個元組
 

首先urls是個函數被@property裝飾器裝飾為屬性,urls屬性返回的是一個元組【元組的第一個元素是列表,由get_urls函數返回】

get_urls函數中定義了一個urlpatterns並最終返回,里面有url路徑和視圖函數的對應關系,這些默認register(admin)后自帶的,所以在urls.py 執行

?
1
url(r '^admin/' , admin.site.urls)   

會找到注冊的admin類,為每一個類生成N個URL(如上urlpattern中定義的)

如果用戶自己注冊的模塊,如下

?
1
admin.site.register(models.UserInfo)  # 注冊的時候沒有傳入admin_class,默認使用的是ModelAdmin 

會根據下面的代碼添加到urlpatterns中

for model, model_admin in self._registry.items():
    urlpatterns += [
        url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
    ]

model_class._meta.app_label 代表model所在的模塊名比如app01
model_class._meta.model_name 代表model對應的表的小寫名,比如userinfo

這樣就生成了/app01/userinfo/ 這樣的url前綴。

后面調用的include(model_admins.urls) 中的model_admins.urls 即執行的是self._registry['UserInfo'].urls ===== admin_class(model, self).urls=======admin_class這個類生成對象的urls屬性【此時這個對象是UserInfo的對象,所以下面get_url函數中的self指的就是UserInfo的對象】======admin_class這個類的urls屬性========= > ModelAdmin這個類的urls屬性【因為注冊的時候沒有傳入admin_class,所以默認是ModelAdmin

再來看下ModelAdmin這個類的urls屬性

 
def get_urls(self):
    from django.conf.urls import url

    def wrap(view):
        def wrapper(*args, **kwargs):
            return self.admin_site.admin_view(view)(*args, **kwargs)
        wrapper.model_admin = self
        return update_wrapper(wrapper, view)

    info = self.model._meta.app_label, self.model._meta.model_name

 urlpatterns = [
        url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info),
        url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),     # self代表的是UserInfo的對象
        url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
        url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
        url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
        # For backwards compatibility (was the change url before 1.9)
        url(r'^(.+)/$', wrap(RedirectView.as_view(
            pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
        ))),
    ]
    return urlpatterns

@property
def urls(self):
    return self.get_urls()
 

同理,urls是個被@property裝飾的屬性,調用ModelAdmin類自身的get_urls函數的時候返回的即是上面紅色的urlpatterns【這也是django admin為什么注冊了一個類之后就為這個類提供了基本url和函數映射的基本原因】,這樣和上面形成的url路徑拼接即可得到如下的效果

復制代碼
urlpatterns = [
        url(r'admin/app01/userinfo/', wrap(self.changelist_view), name='%s_%s_changelist' % info),
        url(r'admin/app01/userinfo/add/$', wrap(self.add_view), name='%s_%s_add' % info),        # self 代表的是UserInfo
        url(r'admin/app01/userinfo/(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
        url(r'admin/app01/userinfo/(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
        url(r'admin/app01/userinfo/(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
        # For backwards compatibility (was the change url before 1.9)
        url(r'admin/app01/userinfo/(.+)/$', wrap(RedirectView.as_view(
            pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
        ))),
    ]
復制代碼

當然也可以自定制ModelAdmin,如下

 
from django.contrib import admin
from django.contrib.admin import ModelAdmin
from . import models
from django.shortcuts import HttpResponse

class UserInfoModelAdmin(ModelAdmin):
# 沒有__super__ 是這個類只定義了一個方法,沒有繼承其他的?還是繼承了所有,只覆蓋了這一個呢?
# 自定義功能 def changelist_view(self, request,
*args,**kwargs): return HttpResponse('用戶列表') admin.site.register(models.UserInfo,UserInfoModelAdmin)
class UserTypeModelAdmin(ModelAdmin): pass admin.site.register(models.UserType,UserTypeModelAdmin)
 

繼承自ModelAdmin,並重寫了changelist_view 這個方法,父類 其他的繼承了嗎?==

所以上面這張register的方法可以如下理解:

 
在admin.site對象的 _registry = {}
_registry = {
    models.UserInfo: obj1 = ModelAdmin(models.UserInfo,admin.site),   #obj1 即上面說的UserInfo對象
    /admin/app01/userinfo/                obj1.changelist_view
    /admin/app01/userinfo/add/            obj1.add_view
    /admin/app01/userinfo/(\d+)/delete/   obj1.delete_view
    /admin/app01/userinfo/(\d+)/change/   obj1.change_view
                                          self.model=models.UserInfo


    models.UserType: obj2 = ModelAdmin(models.UserType,admin.site),  #obj2 和上面說的同理,代表的是UserType對象
    /admin/app01/usertype/                obj2.changelist_view
    /admin/app01/usertype/add/            obj2.add_view
    /admin/app01/usertype/(\d+)/delete/   obj2.delete_view
    /admin/app01/usertype/(\d+)/change/   obj2.change_view
                                          self.model=models.UserType
}

for model, model_admin in self._registry.items():
    urlpatterns += [
        # url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
        url(r'^%s/%s/' % ('app01', 'userinfo'), include(model_admin.urls)),
    ]
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM