内容回顾:
一 admin的使用
app01的admin.py文件:
class BookConfig(admin.ModelAdmin):
list_display=[]
list_display_links=[]
list_filter=[]
search_fields=[]
def patch(self,request,queryset):
pass
patch.short_desc=""
actions=[patch,]
admin.site.register(Book,BookConfig)
二 admin的源码解析
1 启动
加载settings中install_app
from django.contrib import admin
autodiscover_modules(‘admin‘)
2 注册
源码:django.contrib.admin.sites模块
class AdminSite(object):
def __init__(self):
self._registry = {}
def register(self,model,admin_class=None):
# 设置配置类
if not admin_class:
admin_class = ModelAdmin
self._registry[model] = admin_class(model, self)
site = AdminSite()
每一个app下的admin.py文件:
from django.contrib import admin
admin.site.register(Book,BookConfig)
admin.site.register(Publish)
我们先来说一下admin组件中的第三步,他是如何设计url的
我们之所以能进去admin后台管理界面,是因为有这个url:
我们之前配url时,是这样配置的 url(r"index/",views.index),但是对于admin中,一张表就会对应有多个url,就拿Food表来说:
针对Food表,url:
http://127.0.0.1:8000/admin/app02/food/
http://127.0.0.1:8000/admin/app02/food/add/
http://127.0.0.1:8000/admin/app02/food/1/change/
http://127.0.0.1:8000/admin/app02/food/2/delete/
那么admin是怎么实现只配那么一条url呢?其实我们能够想到二级路由,但这里不是二级路由实现的。
我们先来学习一个知识点:
我们这样配url:
‘test/’url后面不写视图函数,后面写一个元祖,元祖里面有三个参数,第一个元祖是一个列表,第二个参数,和第三个参数我们暂时用不到,所以写None。而这个列表中写第二个url,然后我们在app01下的视图函数中写函数aaa。我们可以写的更复杂一点,可以仿照admin中的url写。
url(r‘^stark/‘, ([ url(r‘app01/‘, ([ url(r‘food/‘, ([ url(r‘^$‘, listview), url(r‘add/$‘, addview), url(r‘(\d+)/change/$‘, changeview), url(r‘(\d+)/delete/$‘, delview), ], None, None))], None, None)) ], None, None))
其实admin后面的admin.site.urls就是实现了这样的url的配置。
这样写肯定也是不对的,也因为我们有很多表,会在不同的app下,当我们点击相应的表会显示相应的app和相应的表的内容,实现一个动态的效果。那么我们怎么来获取相应的app名和表的名呢?
我们在注册的时候不就是往admin.site._registry中添加键值对吗,那个键值对里面刚好有我们需要的。我们可以打印出来看看。
先在modes中写几张表,然后再admin中注册。
def get_urls(): temp=[] print(admin.site._registry) return temp urlpatterns = [ # url(r‘^admin/‘, admin.site.urls), url(r‘^admin/‘,(get_urls(),None,None)) # 这里把元祖中的第一个列表参数写成了一个函数,函数仍然返回的是一个列表,在函数中我们打印admin.site._registry键值对。 ]
这是打印的一条结果,他的健是models中的Publish表对象,他的值为admin中自定义写的PublishConfig类对象,那我们怎么取我们想要的东西呢?这里介绍两个方法:
model_name = model._meta.model_name # 获取当前model表名 app_label = model._meta.app_label # 获取所在app的名字 print(model_name) print(app_label)
既然能够拿到model表的名字和app名,我们就能够实现表的一个动态的增删改查:
接下来我们仿照admin实现一个自定义的增删改查的组件
我们新建一个项目,然后再项目里新建另一个app,起名为stark,作为我们的组件。我们自己写组件的时候,要先理解admin组件是怎么工作的,要清楚admin组件中的流程。不然会很难理解。(别忘了创建stark的app后,将stark添加进settings中的INSTALLED_APPS)。
我们就是要做类似于admin源码里面的那三步,将那三步写进我们自己的stark组件中去。
我们先按照admin的流程来想,我们每个app下面都会有admin.py文件,当我们启动django时,会执行app下的admin.py文件, 这时候是因为执行了源码中的:
def autodiscover(): autodiscover_modules(‘admin‘, register_to=site)
那么我们就想要让他启动时先执行我们自己创建的stark.py文件,也应该执行这句代码。我们先在app01下创建stark.py文件。那这句代码应该放在哪呢?
我们将stark添加进settings中的INSTALLED_APPS,django启动时,会逐个扫描里面的每一个,当他扫描到stark.apps.StarkConfig的时候,会执行StarkConfig,点进去,就会到stark app下的apps.py文件,意思就是说当他扫描到stark.apps.StarkConfig的时候,会首先执行apps.py文件。所以我们要把这句话放在apps.py文件里。
这样写完之后,启动后就可以先执行app01下的stark.py文件中的内容了。
启动完了之后,就是注册了。
admin在注册的时候其实就是执行了AdminSite的一个单例模式:就是执行了这样一段代码:
class AdminSite(object): def __init__(self): self._registry = {} def register(self,model,admin_class=None): # 设置配置类 if not admin_class: admin_class = ModelAdmin self._registry[model] = admin_class(model, self) site = AdminSite()
我们要把这段代码单独放在一个模块里,我们也把这个模块的名字叫做sites:
# 默认的配置类class ModelStark(object): def __init__(self,model): self.model = model # stark组件的全局类 class AdminSite(object): def __init__(self): self._registry = {} def register(self, model, admin_class=None): # 设置配置类 if not admin_class: admin_class = ModelStark self._registry[model] = admin_class(model) site = AdminSite()
这样就可以了。
接下来 ,我们在model里面放几张表。我们之前在admin中注册的时候是这样写的:
from django.contrib import admin # Register your models here. from app01.models import Book,Publish,Author,AuthorDetail # 定义自己的类 class BookConfig(admin.ModelStark): # list_display‘ must not be a ManyToManyField. list_display=["title","price","publishDate","publish"] list_display_links = ["price","title"] list_filter = ["title","publish","authors"] search_fields = ["title","price"] # 批量操作 def patch_init(self,request,queryset): queryset.update(price=0) patch_init.short_description = "价格初始化" actions =[patch_init] admin.site.register(Book,BookConfig) class PublishConfig(admin.ModelAdmin): list_display = ["name","email"] admin.site.register(Publish,PublishConfig) admin.site.register(Author) admin.site.register(AuthorDetail)
也就是说要在我们自己app01下的stark.py下进行注册。就应该这样写:
from stark import sites from app01 import models sites.site.register(models.Book) sites.site.register(models.Publish) sites.site.register(models.AuthorDetail) sites.site.register(models.Author)
当然我们也可以添加自己的类:
from stark import sites from app01 import models # 定义自己的类 class Bookconfig(sites.ModelStark): # list_display‘ must not be a ManyToManyField. list_display = ["title", "price", "publishDate", "publish"] list_display_links = ["price", "title"] list_filter = ["title", "publish", "authors"] search_fields = ["title", "price"] # 批量操作 def patch_init(self, request, queryset): queryset.update(price=0) patch_init.short_description = "价格初始化" actions = [patch_init] sites.site.register(models.Book,Bookconfig) sites.site.register(models.Publish) sites.site.register(models.AuthorDetail) sites.site.register(models.Author)
这样就注册好了。
这时候我们也可以print(sites.site._registry)看看里面有几个键值对。
接下来就是第三部分,设计url了:
之前我们已经写好了url,但是:
urlpatterns = [ # url(r‘^admin/‘, admin.site.urls), url(r‘^admin/‘,(get_urls(),None,None)) ]我们写的这个url和admin中的不一样,我们也希望将自己写的url放进我们写的stark组件中。我们应该这样写:
from stark import sites urlpatterns = [ url(r‘^stark/‘,sites.site.urls) ]这样写那说明,site的那个实例化类中要有urls的这个方法,只要这个urls方法返回的是([],None,None),接下来我们就要在那个类中添加这个方法:
def get_urls2(self): temp = [ url(r‘^$‘, listview), url(r‘add/$‘, addview), url(r‘(\d+)/change/$‘, changeview), url(r‘(\d+)/delete/$‘, delview), ] return temp def get_urls(self): temp = [] for model, config_obj in self._registry.items(): # 循环分别打印健和值。 model_name = model._meta.model_name # 获取当前model的表名 app_label = model._meta.app_label # 获取所在的app名字 print(model_name) print(app_label) temp.append(url(r‘%s/%s/‘ % (app_label, model_name), (self.get_urls2(), None, None))) return temp @property def urls(self): return self.get_urls(),None,None
在AdminSite()类中添加这些东西,我们觉得这样就成功了,但是一级url的分发(get_urls())和二级url的分发( get_urls2()),是不能放在一个类中的,因为我们的二级url分发是需要访问视图函数的,那么我们怎么能够保证当我们点击书籍的时候就访问书籍的列表访问publish就展示publish的列表,如果我们这样写,就只能进入一个视图函数了。所以二级url的分发不能放在这个类里,那我们把这个二级url的分发放在哪里呢?放在那个配置类里面去
将二级url的分发放在配置类中有什么好处?config_obj是我们的配置类, 可以是默认的配置类也可以是自定义的配置类。在这里我们要明白这个self指的是谁,他指的是当前这个配置类,而这个配置类中有就有参数model。虽然访问的时候都要进相同的视图函数,但是每次访问不同的表时的配置类不一样,所以self就不一样,进入的视图函数也就不一样。
这样了解之后,就可以完善视图函数了:
def listview(self,request): print(self)# 当前访问摩星表的配置类对象 print(self.model)# 当前访问的模型表 data_list = self.model.object.all() return render(request,‘list.html‘,locals())
原文地址:https://www.cnblogs.com/yb635238477/p/9551195.html