admin_class(model, self)
#备注:截取的源码中的一部分
示例site对象时,会生成一个self._registry={}这个对象属性,如果我们没有指定admin_class,那么admin_class就是ModelAdmin,由此可以看出,admin_class是admin提供给我们定制页面的一个自定义类,这个类必须继承ModelAdmin这个类,以下是:
class ModelAdmin(BaseModelAdmin):
"Encapsulates all admin options and functionality for a given model."
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
list_select_related = False
list_per_page = 100
list_max_show_all = 200
list_editable = ()
search_fields = ()
date_hierarchy = None
save_as = False
save_as_continue = True
save_on_top = False
paginator = Paginator
preserve_filters = True
inlines = []
# Custom templates (designed to be over-ridden in subclasses)
add_form_template = None
change_form_template = None
change_list_template = None
delete_confirmation_template = None
delete_selected_confirmation_template = None
object_history_template = None
popup_response_template = None
# Actions
actions = []
action_form = helpers.ActionForm
actions_on_top = True
actions_on_bottom = False
actions_selection_counter = True
checks_class = ModelAdminChecks
def __init__(self, model, admin_site):
self.model = model
self.opts = model._meta
self.admin_site = admin_site
super(ModelAdmin, self).__init__()
def __str__(self):
return "%s.%s" % (self.model._meta.app_label, self.__class__.__name__)
所以不管有没有自定制样式类,都会执行self._registry[model] = admin_class(model, self),也就是说,注册一个模型类,就会在对象的_registry这个字典中添加一个键值对,这个键是我们注册的这个模型类 model,值是继承ModelAdmin的样式类或者ModelAdmin这个类的一个对象。
3,设计url
因为site是一个单例对象,所以admin在执行完所有admin.py文件后,就会得到整个全局的一个包含所有注册模型的字典_registry,得到这个self._registry(也就是admin.site对象)字典,我们可以for循环这个admin.site._registry字典,得到这些键值对,那么得到这些键值对有什么用呢?这就是admin设计url时会用到的。
我们访问admin这个后台管理页面会发现,这个页面关于我们注册的所有模型都会实现增删改查功能,每个功能界面对应不同的模型类时,除了数据不同外,是一样的,也就是说,admin对于不同的模型类用到是一套url,一套模板,那么这个url是怎么设计的呢?
我们分别访问不同的模型类的这四个功能页面,会发现一个规律:
查询页面url: http://IP:PORT/admin/app名/模型类的名字(全部小写)/
添加页面url: http://IP:PORT/admin/app名/模型类的名字(全部小写)/add
编辑页面url:http://IP:PORT/admin/app名/模型类的名字(全部小写)/id值/change
删除页面url:http://IP:PORT/admin/app名/模型类的名字(全部小写)/id值/delete
通过这个规律可以看出url分发的实现
但是在这个实现的过程中,我们有一个需要注意的时,用户访问的请求携带的路径是一个字符串的类型,我们可以通过request.path得到用户访问的路径,也就能得到用户访问的是哪个APP下的哪个模型类,但是这两个参数都是字符串的类型,如何通过字符串得到用户访问的哪张表,这是个麻烦,这时,可能我们会想到使用importlib模块来得到这个类,但是,这样这个过程很麻烦,Django为我们封装好了相应的方法:我们可以通过这个模型类的类名,通过._meta.model_name这个方法得到对应的字符串形式的类名,同样的我们也可以通过这个类名._meta.app_label得到字符串格式的相应的APP名
# 使用model代指模型类的类名
model._meta.model_name #得到字符串格式的类名(全小写的)
model._meta.app_label #可以得到当前类所在APP的字符串的名字
# 补充
model._meta.get_field("字符串格式的字段属性") # 得到一个字段属性的对象field_obj,这样我们就可以利用这个字段对象取得属性
# 比如: field_obj.verbose_name
通过这两个封装的方法,就完美的解决了我们头疼的问题
from django.conf.urls import url
from django.contrib import admin
def get_urls_operate():
emp = []
emp.append(url(r'^$',查询页面的视图函数))
emp.append(url(r'^add/$',添加页面的视图函数))
emp.append(url(r'^(\d+)/change/$',编辑页面的视图函数))
emp.append(url(r'^(\d+)/delete/$',删除页面的视图函数))
return emp
def get_urls():
temp = []
for model,main_class_obj in admin.site._registry.items():
app_name = model._meta.app_label
model_name = model._meta.model_name
temp.append(url(r'^{}/{}/'.format(app_name,model_name),(get_urls_operate(),None,None))) #实现第二层路由的分发
return temp
urlpatterns = [
url(r'^Xadmin/',(get_urls(),None,None)) #自定义一个前缀 实现第一层路由的分发
这样我们就实现一个通过一个路由实现不同场景的分发
通过这三部分,我们可以按照admin的实现方式,来自定制Xadmin
我们了解了admin内部的实现流程,我们可以将这个实现过程封装到一个类中。
我们自定制Xadmin时,也要按照admin的流程实现,首先是启动项,admin中,Django启动时,会自动的执行每个app下的admin.py文件,我们可以自定制为启动时,自动执行app下的每个自定制的.py文件,比如Xadmin.py 文件,那么如何达到Django启动的时候帮我们自动扫描加载我们自定制的Xadmin.py文件呢?这是个问题
我们观察可以发现,Django在启动时,会加载配置文件settings.py ,在settings.py文件中,有一个INSTALLED_APPS这个列表,这个列表中,放置着我们在Django项目中的所有app的配置信息,我们观察这个列表:我们自己开启的APP,在设置配置信息时,会在APP名字后加一个.apps.(app名首字母大写)Config这个东西,而对应的在我们的app下,Django会给我们自动的配置一个apps.py文件,那么这个apps.py文件有什么作用呢,我们打开这个apps.py文件,看看里面的配置信息:
以blogs这个APP为例:
from django.apps import AppConfig
class BlogsConfig(AppConfig):
name = 'blogs'
我们可以发现,里面定义了一个类,这个类的类名就是settings配置信息中apps后面跟的那个东东,这个类中有一个静态属性name是当前的APP名,这个类继承了AppConfig这个类。这就是我们从这个.py文件中所能得的所有东西,乍一看,没有什么有用的信息,那么我们只能从它继承的类中找了
MODELS_MODULE_NAME = 'models'
class AppConfig(object):
Class representing a Django application and its configuration.
def __init__(self, app_name, app_module):
# Full Python path to the application eg. 'django.contrib.admin'.
self.name = app_name
def ready(self):
Override this method in subclasses to run code when Django starts.
#这句话的语义为:在子类中重写此方法,以便在Django启动时运行代码
查看整个AppConfig,我们可以把我们的要启动的代码放置在重写的ready方法中即可
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules
class XadminConfig(AppConfig):
name = 'Xadmin'
def ready(self):
autodiscover_modules('Xadmin') #Django启动时会自动扫描每个app下的Xadmin.py文件
这样,我们就完成了自定制Xadmin的启动阶段,启动后我们就要进行下一步注册
在每个app下的Xadmin.py 文件中注册模型
以blogs这个APP下的UserInfo、Book为例:
from blogs import models
from Xadmin.service.Xadmin import site
site.register(models.UserInfo)
site.register(models.Book)
接下了就是设计url了,我们可以仿照admin的方式,在单例对象admin.site的这个类中封装好这些方法
from django.conf.urls import url
from django.shortcuts import HttpResponse,render
class ModelXadmin(object):
def __init__(self,model,site):
self.model = model
self.site = site
def show(self,request):
data_list = self.model.objects.all()
return render(request,'show.html',locals()) #locals() 请函数内部所有的键值对存储 == {"data_list":data_list}
def add(self,request):
return HttpResponse('添加页面')
def edit(self,request, pk):
return HttpResponse('编辑页面')
def delete(self,request, pk):
return HttpResponse('删除页面')
@property
def get_urls_operate(self):
emp = []
emp.append(url(r'^$', self.show))
emp.append(url(r'^add/$', self.add))
emp.append(url(r'^(\d+)/change/$', self.edit))
emp.append(url(r'^(\d+)/delete/$', self.delete))
return emp
@property
def urls(self):
return self.get_urls_operate,None,None
class XadminSite(object):
def __init__(self,name='xadmin'):
self._registry = {}
def register(self,model,class_main=None,**option):
if not class_main:
class_main = ModelXadmin
self._registry[model] = class_main(model,self)
@property
def get_urls(self):
# print(admin.site._registry)
temp = []
for model, model_admin_object in self._registry.items():
model_name = model._meta.model_name
model_app = model._meta.app_label
temp.append(url(r'^{}/{}/'.format(model_app, model_name), model_admin_object.urls))
return temp
@property
def urls(self):
return self.get_urls,None,None
site = XadminSite()
备注:一个关键点,为什么把第二层分发设置在了ModelXadmin这个类中,那肯定是放在这个类中能有什么好处,如果我们把这个二级分发放在XadminSite中,那么我们要取得每一个模型的数据是很麻烦的,但是,如果我们把这个放在ModelXadmin中,由于,在register(注册)时,我们给class_admin(XadminSite)传了每一个模型类,所以放在这个类中,我们可以通过self.model这个属性获得每个模型类的数据(self.model.objects.all()),这样就很容易得到这个模型表。