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()
  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/aizhinong/p/12369152.html
Copyright © 2011-2022 走看看