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的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!