Django中自定义admin Xadmin的实现代码
在Django框架中,自带一个后台管理页面admin,这个管理页面很全,但是,有些并不是我们需要的,所以我们可以根据admin的实现流程来自定义自己的需求,即根据admin的实现方式来实现自定制--Xadmin
首先,我们先解析admin的流程,在Django中,我们在创建项目的时候,Django自带一个admin的url,实现了不同模型表的增删改查,那么admin是如何实现url的分发的?
我们可以从三部分来看admin的路由分发实现
1,启动
我们可以通过fromdjango.contribimportadmin来看admin是如何启动的
Django启动后,会在manage.py文件中加载配置文件settings.py ,在settings.py中有一个INSTALLED_APPS这个配置项,Django会按照配置项的内容一次加载每一个app.
#Applicationdefinition 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文件,
fromdjango.contrib.admin.sitesimportAdminSite,site fromdjango.utils.module_loadingimportautodiscover_modules defautodiscover(): autodiscover_modules('admin',register_to=site)
执行auto_discover这个接口,会自动的加载所有APP中的admin.py这个文件,这就是admin的启动文件
2,注册
自动加载所有APP下的admin.py文件时,会一次记录所有执行了admin.site.register(模型类)这个方法的模型类,从而完成模型类的注册。
所有的模型类都执行admin.site.register()这个方法后完成的注册,那么这个方法内部都做了些什么?
我们可以点击site进入admin的源码中,得到这个:site=AdminSite() site是一个对象,是AdminSite这个类的一个对象,并且是一个单例对象,那么我们就可以肯定,register是这个单例对象的一个方法,那这个方法中到底做了什么?
classAdminSite(object): """ AnAdminSiteobjectencapsulatesaninstanceoftheDjangoadminapplication,ready tobehookedintoyourURLconf.ModelsareregisteredwiththeAdminSiteusingthe register()method,andtheget_urls()methodcanthenbeusedtoaccessDjangoview functionsthatpresentafulladmininterfaceforthecollectionofregistered models. """ def__init__(self,name='admin'): self._registry={}#model_classclass->admin_classinstance self.name=name #关于register的方法 defregister(self,model_or_iterable,admin_class=None,**options): ifnotadmin_class: admin_class=ModelAdmin #Instantiatetheadminclasstosaveintheregistry self._registry[model]=admin_class(model,self) #备注:截取的源码中的一部分
示例site对象时,会生成一个self._registry={}这个对象属性,如果我们没有指定admin_class,那么admin_class就是ModelAdmin,由此可以看出,admin_class是admin提供给我们定制页面的一个自定义类,这个类必须继承ModelAdmin这个类,以下是:
classModelAdmin(BaseModelAdmin): "Encapsulatesalladminoptionsandfunctionalityforagivenmodel." 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=[] #Customtemplates(designedtobeover-riddeninsubclasses) 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
通过这两个封装的方法,就完美的解决了我们头疼的问题
fromdjango.conf.urlsimporturl fromdjango.contribimportadmin defget_urls_operate(): emp=[] emp.append(url(r'^$',查询页面的视图函数)) emp.append(url(r'^add/$',添加页面的视图函数)) emp.append(url(r'^(\d+)/change/$',编辑页面的视图函数)) emp.append(url(r'^(\d+)/delete/$',删除页面的视图函数)) returnemp defget_urls(): temp=[] formodel,main_class_objinadmin.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)))#实现第二层路由的分发 returntemp 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为例:
fromdjango.appsimportAppConfig classBlogsConfig(AppConfig): name='blogs'
我们可以发现,里面定义了一个类,这个类的类名就是settings配置信息中apps后面跟的那个东东,这个类中有一个静态属性name是当前的APP名,这个类继承了AppConfig这个类。这就是我们从这个.py文件中所能得的所有东西,乍一看,没有什么有用的信息,那么我们只能从它继承的类中找了
MODELS_MODULE_NAME='models' classAppConfig(object): """ ClassrepresentingaDjangoapplicationanditsconfiguration. """ def__init__(self,app_name,app_module): #FullPythonpathtotheapplicationeg.'django.contrib.admin'. self.name=app_name defready(self): """ OverridethismethodinsubclassestoruncodewhenDjangostarts. #这句话的语义为:在子类中重写此方法,以便在Django启动时运行代码 """
查看整个AppConfig,我们可以把我们的要启动的代码放置在重写的ready方法中即可
fromdjango.appsimportAppConfig fromdjango.utils.module_loadingimportautodiscover_modules classXadminConfig(AppConfig): name='Xadmin' defready(self): autodiscover_modules('Xadmin')#Django启动时会自动扫描每个app下的Xadmin.py文件
这样,我们就完成了自定制Xadmin的启动阶段,启动后我们就要进行下一步注册
在每个app下的Xadmin.py文件中注册模型
以blogs这个APP下的UserInfo、Book为例:
fromblogsimportmodels fromXadmin.service.Xadminimportsite site.register(models.UserInfo) site.register(models.Book)
接下了就是设计url了,我们可以仿照admin的方式,在单例对象admin.site的这个类中封装好这些方法
fromdjango.conf.urlsimporturl fromdjango.shortcutsimportHttpResponse,render classModelXadmin(object): def__init__(self,model,site): self.model=model self.site=site defshow(self,request): data_list=self.model.objects.all() returnrender(request,'show.html',locals())#locals()请函数内部所有的键值对存储=={"data_list":data_list} defadd(self,request): returnHttpResponse('添加页面') defedit(self,request,pk): returnHttpResponse('编辑页面') defdelete(self,request,pk): returnHttpResponse('删除页面') @property defget_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)) returnemp @property defurls(self): returnself.get_urls_operate,None,None classXadminSite(object): def__init__(self,name='xadmin'): self._registry={} defregister(self,model,class_main=None,**option): ifnotclass_main: class_main=ModelXadmin self._registry[model]=class_main(model,self) @property defget_urls(self): #print(admin.site._registry) temp=[] formodel,model_admin_objectinself._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)) returntemp @property defurls(self): returnself.get_urls,None,None site=XadminSite()
备注:一个关键点,为什么把第二层分发设置在了ModelXadmin这个类中,那肯定是放在这个类中能有什么好处,如果我们把这个二级分发放在XadminSite中,那么我们要取得每一个模型的数据是很麻烦的,但是,如果我们把这个放在ModelXadmin中,由于,在register(注册)时,我们给class_admin(XadminSite)传了每一个模型类,所以放在这个类中,我们可以通过self.model这个属性获得每个模型类的数据(self.model.objects.all()),这样就很容易得到这个模型表。
总结
以上所述是小编给大家介绍的Django中自定义adminXadmin的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!