zoukankan      html  css  js  c++  java
  • 包之间的单例模式,模仿django admin开发一个自己的组件

    单例模式的几种方法:

    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')
    apps.py

    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)
    xadmin.py

    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()
    start_xadmin.py

    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) 
    ]
    urls.py

    注意问题:

      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()
  • 相关阅读:
    [转]权限树中Checkbox的操作[Asp.Net2.0]
    [转]IE点击链接没有反应或打开新窗口出现一个空白框(地址栏空白)的解决方法
    [引]VS2005 之 Visual Basic 编程语言介绍
    [文摘20070816]家(周国平)
    Linux 下zip包的压缩与解压
    SOSO发布国内首家高清街景地图 引领地图换代
    VC 获取当前工作目录和执行目录的一些方法
    设置vim 默认显示行号
    利用脚本将文字插入到图片或进行多个图片拼接
    ImageMagick操作合并图像
  • 原文地址:https://www.cnblogs.com/aizhinong/p/12369152.html
Copyright © 2011-2022 走看看