单例模式的几种方法:
1、利用类的双下__new__方法实现单例模式:
每实例化一个对象都会new一次,每个对象都会新建一个新的内存地址,那么可以自定义new方法实现单例模式,即每创建一个对象都继用实例化的第一个对象的内存地址,不管对哪个对象进行操作,都是操作同一个对象
class Teacher: __new_teacher = False #私有化一个属性 def __init__(self,name,sex): self.name = name self.sex = sex def __new__(cls, *args, **kwargs):#实现了不管如何创建对象,都是返回这个私有属性,这个私有属性一直是第一次实例化的对象 if cls.__new_teacher: #如果私有化属性不为空 return cls.__new_teacher #返回这个属性(对象) else: cls.__new_teacher = object.__new__(cls) #否则就创建一个对象赋值给私有化的属性 return cls.__new_teacher #返回这个属性(对象) aike = Teacher('aike','man') aike1= Teacher('aike','man') print(aike)#内存地址一样 print(aike1)#内存地址一样 aike.name = '艾克' print(aike.name)#艾克 print(aike1.name)#艾克 #打印: <__main__.Teacher object at 0x000001C184484D48> <__main__.Teacher object at 0x000001C184484D48> 艾克 艾克
1、利用python导包特性,实现单例模式
Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。后续导入时,模块中的对象内存地址依然是指向第一次导入时的地址。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
class Person(object): def __init__(self, name, age): self.name = name self.age = age p1 = Person("aike", 9000)
from singleton import p1 print(id(p1)) print(p1.name) p1.name = "Bob" from singleton import p1 print(id(p1)) print(p1.name)
admin执行流程
1、循环加载执行所有已经注册的app中的admin.py文件
def autodiscover(): autodiscover_modules('admin', register_to=site)
2、执行代码
#admin.py class BookAdmin(admin.ModelAdmin): list_display = ("title",'publishDate', 'price') admin.site.register(Book, BookAdmin) admin.site.register(Publish)
3、admin.site
这里应用的是一个单例模式,对于AdminSite类的一个单例模式,执行的每一个app中的每一个admin.site都是一个对象
4、执行register方法
admin.site.register(Book, BookAdmin)
admin.site.register(Publish)
class ModelAdmin(BaseModelAdmin):pass 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)
到此,注册结束,然后进行url分发
5、django2.2.10下,admin的URL配置
urlpatterns = [ path('admin/', admin.site.urls), ]
AdminSite类下进行一级分发:

def get_urls(self): from django.urls import include, path, re_path # Since this module gets imported in the application's root package, # it cannot import models from other applications at the module level, # and django.contrib.contenttypes.views imports ContentType. from django.contrib.contenttypes import views as contenttype_views def wrap(view, cacheable=False): def wrapper(*args, **kwargs): return self.admin_view(view, cacheable)(*args, **kwargs) wrapper.admin_site = self return update_wrapper(wrapper, view) # Admin-site-wide views. urlpatterns = [ path('', wrap(self.index), name='index'), path('login/', self.login, name='login'), path('logout/', wrap(self.logout), name='logout'), path('password_change/', wrap(self.password_change, cacheable=True), name='password_change'), path( 'password_change/done/', wrap(self.password_change_done, cacheable=True), name='password_change_done', ), path('jsi18n/', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'), path( 'r/<int:content_type_id>/<path:object_id>/', wrap(contenttype_views.shortcut), name='view_on_site', ), ] # Add in each model's views, and create a list of valid URLS for the # app_index valid_app_labels = [] for model, model_admin in self._registry.items(): urlpatterns += [ path('%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), ] # model_admin.urls进行二级分发 if model._meta.app_label not in valid_app_labels: valid_app_labels.append(model._meta.app_label) # If there were ModelAdmins registered, we should have a list of app # labels for which we need to allow access to the app_index view, if valid_app_labels: regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$' urlpatterns += [ re_path(regex, wrap(self.app_index), name='app_list'), ] return urlpatterns @property def urls(self): return self.get_urls(), 'admin', self.name
ModelAdmin类下进行二级分发:

def get_urls(self): from django.urls import path def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) wrapper.model_admin = self return update_wrapper(wrapper, view) info = self.model._meta.app_label, self.model._meta.model_name urlpatterns = [ path('', wrap(self.changelist_view), name='%s_%s_changelist' % info), path('add/', wrap(self.add_view), name='%s_%s_add' % info), path('autocomplete/', wrap(self.autocomplete_view), name='%s_%s_autocomplete' % info), path('<path:object_id>/history/', wrap(self.history_view), name='%s_%s_history' % info), path('<path:object_id>/delete/', wrap(self.delete_view), name='%s_%s_delete' % info), path('<path:object_id>/change/', wrap(self.change_view), name='%s_%s_change' % info), # For backwards compatibility (was the change url before 1.9) path('<path:object_id>/', wrap(RedirectView.as_view( pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info) ))), ] return urlpatterns @property def urls(self): return self.get_urls()
6、django1下的url()方法的分发,使用方法与django2中的path()同样适用
from django.shortcuts import HttpResponse def test01(request): return HttpResponse("test01") def test02(request): return HttpResponse("test02") urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^xadmin/', ([ url(r'^test01/', test01), url(r'^test02/', test02), ],None,None)), ]

from django.conf.urls import url,include from django.contrib import admin from django.shortcuts import HttpResponse def change_list_view(request): return HttpResponse("change_list_view") def add_view(request): return HttpResponse("add_view") def delete_view(request): return HttpResponse("delete_view") def change_view(request): return HttpResponse("change_view") def get_urls(): temp=[ url(r"^$".format(app_name,model_name),change_list_view), url(r"^add/$".format(app_name,model_name),add_view), url(r"^d+/del/$".format(app_name,model_name),delete_view), url(r"^d+/change/$".format(app_name,model_name),change_view), ] return temp url_list=[] for model_class,obj in admin.site._registry.items(): model_name=model_class._meta.model_name app_name=model_class._meta.app_label # temp=url(r"{0}/{1}/".format(app_name,model_name),(get_urls(),None,None)) temp=url(r"{0}/{1}/".format(app_name,model_name),include(get_urls())) url_list.append(temp) urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^xadmin/', (url_list,None,None)),
自己实现:
1、启动

from django.apps import AppConfig from django.utils.module_loading import autodiscover_modules class XadminConfig(AppConfig): name = 'xadmin' def ready(self): autodiscover_modules('xadmin')
2、app中注册

from xadmin.service.start__xadim import site, ModelAdmin from app01 import models from django.utils.safestring import mark_safe class BookConfig(ModelAdmin): def add_tag_change(self, obj=None, is_header=False): if is_header: # 如果是表头 return '修改操作' return mark_safe('<a href="%s/change">修改</a>'%obj.pk) # 如果是表单数据 def add_tag_delete(self, obj=None, is_header=False): if is_header: return '删除操作' return mark_safe('<a href="%s/delete">删除</a>'%obj.pk) list_display = ['nid', 'title', 'price', add_tag_change, add_tag_delete] class PublishConfig(ModelAdmin): list_display = ['nid', 'title', 'price'] site.register(models.Book, BookConfig) site.register(models.Publish) site.register(models.Author) site.register(models.AuthorDetail)
3、admin.site 服务:url分发、数据展示等

from django.urls import path, re_path # from app01 import views as app01_views from django.shortcuts import render, redirect, HttpResponse class ModelAdmin: list_display = ['__str__'] # 用于自定义显示哪些字段,默认显示__str__ def __init__(self, model, site): self.model = model self.site = site def test(self, request): all_model_data = [] # 用于存放所有条数据,用于前端展示 all_model_obj = self.model.objects.all() # 处理表头数据 header_list = [] for field in self.list_display: if isinstance(field, str): if field == '__str__': # 如果使用默认字段,表头显示为表名大小 var = self.model._meta.model_name.upper() else: field_obj = self.model._meta.get_field(field) # 获取这个字段类,用于获取别名verbose_name,以在前端展示中文表头 # print(field_obj,type(field_obj)) var = field_obj.verbose_name else: var = field(self, is_header=True) # is_header表示是表头 header_list.append(var) # 处理表单数据 for model_obj in all_model_obj: model_data = [] # 用于存放每一条数据 for field in self.list_display: if isinstance(field, str): # 这个字段是否是str类,不是则为函数,用于显示删除和修改 val = getattr(model_obj, field) # 获取model.Book类中的字段类型,field为字符串类型,需要用反射执行 else: val = field(self, obj=model_obj, is_header=False) # 执行函数,返回删除或者修改的标签, is_header表示不是表头, obj用于注册时的修改或者删除标签的链接拼接 model_data.append(val) all_model_data.append(model_data) # 将每一条数据添加到一个大列表 return render(request, 'show_model_data.html', {'all_model_data': all_model_data, 'header_list': header_list}) # return HttpResponse('ok') def add(self, request, id): return HttpResponse('ok') def delete(self, request, id): return HttpResponse('ok') def change(self, request, id): return HttpResponse('ok') @property def get_urls2(self): url_list = [ re_path('^$', self.test), re_path('(?P<id>d+)/add/', self.add), re_path('(?P<id>d+)/delete/', self.delete), re_path('(?P<id>d+)/change/', self.change), ] return url_list @property def urls2(self): return self.get_urls2, None, None class XadminSite: def __init__(self): self._registry = {} # {注册的model类对象: 这个类对象的配置类} @property def get_urls(self): url_list = [] for model, model_admin in self._registry.items(): app_model = model._meta.app_label # app字符串名字 model_str = model._meta.model_name # 表名字符串名字 url_list.append(path('%s/%s/' % (app_model, model_str), model_admin.urls2)) return url_list @property def urls(self): return self.get_urls, None, None def register(self, model, admin_class=None): if not admin_class: admin_class = ModelAdmin # 没有设配置类时,配置类默认为modelAdmin self._registry[model] = admin_class(model, self) # {Book:admin_class} site = XadminSite()
4、url

from django.contrib import admin from xadmin.service.start__xadim import site from django.urls import path, re_path urlpatterns = [ path('admin/', admin.site.urls), path('xadmin/', site.urls) ]
注意问题:
1、单例模式,每个用户进来都是走site这一个对象
2、每个注册过来的model将以{注册的model类对象: 这个类对象的配置类}字典的形式存放在_registry属性当中,如果不传配置类,将使用默认的配置值类
class XadminSite: def __init__(self): self._registry = {} # {注册的model类对象: 这个类对象的配置类} def register(self, model, admin_class=None): if not admin_class: admin_class = ModelAdmin # 没有设配置类时,配置类默认为modelAdmin self._registry[model] = admin_class(model, self) # {Book:admin_class} site = XadminSite()
class ModelAdmin: list_display = ['__str__'] # 用于自定义显示哪些字段,默认显示__str__ def __init__(self, model, site): self.model = model self.site = site
3、关于url二级分发的问题,第二级分发为什么要放在配置类ModelAdmin中,而不是与第一级分发一样放在XadminSite中
因为第一级分发时,循环_registry字典获取到了每一个model类对象,取到了它所在的app名与表名,当注册多少张表时,就能得到多少条一级url,取到model类对象的同时,还能取到相对应的配置类ModelAdmin,该配置类在注册时传有两个参数,一个model,即model类对象,一个self,即site单例对象。
此时,我们将二级分发在ModelAdmin中实现,可以直接用当前model类对象对应的配置类调用这个二级分发方法。而二级分发后属于一个完整的url,需要紧跟着实现视图功能。这时,由于配置ModelAdmin中已经有两个类属性,一个是当前访问的model类对象,一个site对象。这时便可以直接利用这个model类对象获取数据库中的数据。
4、获取app名、表名与字段别名verbose_name的方法:_meta
field_obj = self.model._meta.get_field(field) # 获取这个字段类,用于获取别名verbose_name,以在前端展示中文表头:参数为一个字段名字的字符串形式,返回这个字段对象,可用于 app_model = model._meta.app_label # app字符串名字 model_str = model._meta.model_name # 表名字符串名字
其中get_field方法接收一个字段的字符串形式,返回这个字段对象。这个字段对象的属性都能获取,例如max_length,verbose_name等,如果是关系字段,则可以获取关联表的所有对象:
field_obj = models.Book._meta.get_field(filter_field) queryset_list = field_obj.remote_field.model.objects.all() # queryset_list = fields_obj.rel.to.objects.all()