在Django框架中,自带一个后台管理页面admin,这个管理页面很全,可是,有些并非咱们须要的,因此咱们能够根据admin的实现流程来自定义本身的需求,即根据admin的实现方式来实现自定制--Xadminhtml
首先,咱们先解析admin的流程,在Django中,咱们在建立项目的时候,Django自带一个admin的url,实现了不一样模型表的增删改查,那么admin是如何实现url的分发的?django
咱们能够从三部分来看admin的路由分发实现session
1,启动app
咱们能够经过from django.contrib import admin来看admin是如何启动的框架
Django启动后,会在manage.py文件中加载配置文件settings.py ,在settings.py中有一个INSTALLED_APPS这个配置项,Django会按照配置项的内容一次加载每个app.ide
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'Xadmin.apps.XadminConfig', 'blogs.apps.BlogsConfig', 'bigs.apps.BigsConfig', ]
加载到django.contrib.admin时,会加载admin相关的,咱们点击admin,进入admin的__init__.py文件,函数
from django.contrib.admin.sites import AdminSite, site from django.utils.module_loading import autodiscover_modules def autodiscover(): autodiscover_modules('admin', register_to=site)
执行auto_discover这个接口,会自动的加载全部APP中的admin.py这个文件,这就是admin的启动文件this
2,注册url
自动加载全部APP下的admin.py文件时,会一次记录全部执行了admin.site.register(模型类)这个方法的模型类,从而完成模型类的注册。spa
全部的模型类都执行admin.site.register()这个方法后完成的注册,那么这个方法内部都作了些什么?
咱们能够点击site进入admin的源码中,获得这个:site = AdminSite() site是一个对象,是AdminSite这个类的一个对象,而且是一个单例对象,那么咱们就能够确定,register是这个单例对象的一个方法,那这个方法中到底作了什么?
class AdminSite(object): """ An AdminSite object encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf. Models are registered with the AdminSite using the register() method, and the get_urls() method can then be used to access Django view functions that present a full admin interface for the collection of registered models. """ def __init__(self, name='admin'): self._registry = {} # model_class class -> admin_class instance self.name = name # 关于register的方法 def register(self, model_or_iterable, admin_class=None, **options): if not admin_class: admin_class = ModelAdmin # Instantiate the admin class to save in the registry self._registry[model] = 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()),这样就很容易获得这个模型表。
原文出处:https://www.cnblogs.com/zhaopanpan/p/9124012.html