zoukankan      html  css  js  c++  java
  • 24.stark组件全部

    admin组件

    admin参考:https://www.cnblogs.com/yuanchenqi/articles/8323452.html

    model参考:https://www.cnblogs.com/yuanchenqi/articles/8963244.html

    # admin的参数,以及自定义显示
    from django.contrib import admin
    from app01.models import *
    class BookConfig(admin.ModelAdmin):
        list_display = ["nid", "title", "price", "publishDate"]  # 定制显示那些列,不能放多对多
        list_display_links = ["title"]  # 查看链接
        # list_filter = ["title", "publishDate", "authors"]  # 过滤
        list_editable = ["price"]  # 编辑
        search_fields = ["title", "price"]  # 搜索字段
        # date_hierarchy = "publishDate"
        fields = ("title",)   # 限定现实的字段
        exclude = ("",)   # 不显示哪些字段,和fields相反
        ordering = ("nid",)  # 排序
        def patch_action(self, request, queryset):
            queryset.update(publishDate="2019-11-22")
        patch_action.short_description = "批量初始化"
        actions = [patch_action]
    admin.site.register(Book, BookConfig)
    admin.site.register(Author)
    admin.site.register(AuthorDetail)
    admin.site.register(Publish)
    

    url使用:

    # url的嵌套
    from django.contrib import admin
    from django.http import HttpResponse
    from django.urls import path
    def yuan(request):
        return HttpResponse("ok")
    def test01(request):
        return HttpResponse("test01")
    from starkapp.service.stark import site
    urlpatterns = [
        path('admin/', admin.site.urls),
        url(r"^stark/", site.urls),
        url("^yuan/", ([
            url(r"^test01/", ([
                 url(r"^test001/", test01),
                url(r"^test002/", test01),
            ], None, None),
                url(r"^test02/", test01),
                url(r"^test03/", test01),
                       ], None, None))  # 第一个是namespace, 第二个是app_name,
    ]
    

    单例模式:

    # 单例模式 (设计模式所有语言都有)
    单例类:就是只允许类生成一个对象,所有的修改对这一个对象的修改,保证数据的统一性
    class Singleton(object):
        _instance = None
        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
            return cls._instance
    class MyClass(Singleton):
        a = 1
    one = MyClass()
    one.a = 2
    two = MyClass()
    print(two.a)
    
    python天生就只吃单例模式的,为什么?
    写一个类,实例化一个对象,将其放入一个包中,其他地方使用的时候直接从这个里面导入(这个时候会生成一个.pyc,之后的每次使用这个对象都是从这个.pyc中取,可以理解为缓存),每次导入到的都是同一个对象,(内存地址相同)。
    

    ### admin源码:
    # 使用
    path('admin/', admin.site.urls),
    admin.site.register(Book, BookConfig)
        def register(self, model_or_iterable, admin_class=None, **options):
            admin_class = admin_class or ModelAdmin
            if isinstance(model_or_iterable, ModelBase):
                model_or_iterable = [model_or_iterable]
     
                    self._registry[model] = admin_class(model, self)
    
    1.注册admin.py
    	admin.site.register(Book, BookConfig)
        def register(self, model_or_iterable, admin_class=None, **options):
        	if not admin_class:
                return admin_class=ModelAdmin
           	self._registry[model] = admin_class(model, self)
    2.url设计
    	如何获取app名称和model名称
    	model._meta.app_label, model._meta.model_name
    一次分发
    	def get_urls():
        temp = []
        for model, admin_class in admin.site._registry.items():
        	# url(r"app01/book/", )
            temp.append(url(r"^%s/%s" %(model._meta.app_label, model._meta.model_name), yuan))
        return temp
        
        urlpatterns = [
            path('admin/', admin.site.urls),
            url(r"^stark/", site.urls),
            url("^yuan/", (get_urls(), None, None))
        ]
    二次分发:
    def add(request,):
        return HttpResponse("add")
    def delete(request,id):
        return HttpResponse("delete")
    def change(request,id):
        return HttpResponse("change")
    def list_view(request,):
        return HttpResponse("list_view")
    def get_urls_2():
        temp = []
        temp.append(url(r"^add/", add))
        temp.append(url(r"^(\d+)/delete/", delete))  # 函数自己定义
        temp.append(url(r"^(\d+)/change/", change))
        temp.append(url(r"^", list_view))
    
        return temp
    def get_urls():
        temp = []
        for model, admin_class in admin.site._registry.items():
            temp.append(url(r"^%s/%s/" %(model._meta.app_label, model._meta.model_name), (get_urls_2(), None, None)))
        return temp
    urlpatterns = [
        path('admin/', admin.site.urls),
        url(r"^stark/", site.urls),
        url("^yuan/", (get_urls(), None, None))
    ]
    

    stark组件:

    查看页面:
    	表头
    	表数据
    	search
    	action
    

    直接给出参考博客网址:https://www.cnblogs.com/smallmars/p/8657094.html

    看不懂的话这里有视频:https://www.bilibili.com/video/av58974459?from=search&seid=4711332916584502531

    这篇博客开始部分不好开头,我把开头整理一下,关于老男孩的项目有了规律,就是你发现你现在看的项目没有视频,那么可能是前几期教的,直接去B站搜这个项目名称相关的视频,就可以找,我是直接看博客,看到多级过滤的时候看不下去了才发现的,第九期的视频里面有很多好的项目可以看。

    基础版本:

    第一步:创建一个项目,去包里面最少有两个app(一个是stark组件的,一个是其他用来测试的stark组件是否产生工作的),我创建了app01和stark组件的app(starkapp)名字可以任意起只要你能找到就行
    第二步:首先settings中注册:
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'starkapp.apps.StarkappConfig',
        'app01.apps.App01Config',
    ]
    第三步:
    starkapp下面创建一个service的包,记住是包,不是的话可能出现问题。同时创建一个stark.py文件
    
    第四步:
    在stark.py中写我们的程序
    from django.conf.urls import url
    from django.http import HttpResponse
    from django.shortcuts import render
    
    
    class ModelStark(object):
        list_display = []
    
        def __init__(self, model, site):
            self.model = model  # 当前用户访问的模型表
            self.site = site
    
        # 记住这个视图函数,如果下面的get_url_2中你没有添加它的话,你根本看不到只记得页面
        def change_list(self, request):
            ret = self.model.objects.all()
            return render(request, "index.html", locals())
    
        def add(self, request):
            return HttpResponse("add")
    
        def delete(self, request, id):
            return HttpResponse("delete")
    
        def change(self, request, id):
            return HttpResponse("change")
    
        # def list_view(self, request):
        #     return HttpResponse("list_view")
    
        def get_urls_2(self):
            temp = []
            model_name = self.model._meta.model_name  # 当前模型表
            app_label = self.model._meta.app_label  # 当前app
    
            temp.append(url(r"^add/$", self.add, name="%s_%s_add" % (app_label, model_name)))
            temp.append(url(r"^(\d+)/delete/$", self.delete, name="%s_%s_delete" % (app_label, model_name)))
            temp.append(url(r"^(\d+)/change/$", self.change, name="%s_%s_change" % (app_label, model_name)))
            temp.append(url(r"^$", self.change_list, name="%s_%s_list" % (app_label, model_name)))
            return temp
    
        @property
        def urls_2(self):
            return self.get_urls_2(), None, None  # [], None, None
    
    
    class StarkSite(object):
    
        def __init__(self):
            self._registry = {}
    
        def register(self, model, stark_class=None, **options):
            if not stark_class:
                # 如果注册的时候没有自定义配置类,执行
                stark_class = ModelStark   # 配置类
    
            # 降配置类对象加到_registry字典中,建立模型类
            self._registry[model] = stark_class(model, self)   # _registry={'model':stark_class(model)}
    
        def get_urls(self):
            """构造一层url"""
            temp = []
            for model, stark_class_obj in self._registry.items():
                # model:一个模型表
                # stark_class_obj:当前模型表相应的配置类对象
                model_name = model._meta.model_name
                app_label = model._meta.app_label
                # 分发增删改查url
                temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2))
                """
                   path('app01/user/',UserConfig(User,site).urls2),
                   path('app01/book/',ModelStark(Book,site).urls2),
                """
            return temp
    
        @property
        def urls(self):
            return self.get_urls(), None, None
    
    
    site = StarkSite()  # 生成一个单例模式的对象
    
    第五步:
    有没有发现自己的项目中urls没配stark app的路由,现在就去
    from django.conf.urls import url
    from django.contrib import admin
    from django.urls import path
    
    from starkapp.service.stark import site
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        url(r"^stark/", site.urls),  # 有没有发现这个site是我们自己写的呢个单例模式的对象,没错就是它
    ]
    
    第六步:
    我们要是用stark组件为我们的模型,创建增删改查页面,我们是不是应该去自己的项目app01了
    
    ```python 要是用这个模块我们就需要创建一个stark1.py(这个名字可以改,看完你就知道咋改了)在自己得项目里面,内容是这样的:

    from starkapp.service.stark import site, ModelStark
    from django.utils.safestring import mark_safe
    from django.urls import reverse
    from .models import *

    自定义配置类

    class BookStark(ModelStark):
    pass

    list_display = ["nid", "title", "price", edit, delete]
    

    site.register(Book, BookStark) # 初始的你只需要把BookStark类定义了,使用我们site注册了就行,里面的内容可以直接pass

    
    ```html
    第七步:
    前段的页面也给你:
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
    </head>
    <body>
    <h3>数据展示</h3>
    
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <table class="table table-striped table-hover">
                    <heade>
                        <tr>
                            <td>id</td>
                            <td>标题</td>
                            <td>价格</td>
                        </tr>
                    </heade>
    
                    {% for obj in ret %}
                        <tr>
                            <td>{{ obj.nid }}</td>
                            <td>{{ obj.title }}</td>
                            <td>{{ obj.price }}</td>
                        </tr>
                    {% endfor %}
                </table>
            </div>
        </div>
    </div>
    </body>
    </html>
    
    
    第八步:
    有没有发现,我们什么都写了,但是咋么让django找到我第六步创建的stark1.py呢?
    现在回到stark组件app下面,在apps.py里面写下面的代码:
    from django.apps import AppConfig
    from django.utils.module_loading import autodiscover_modules
    class StarkappConfig(AppConfig):
        name = 'starkapp'
    
        # 程序启动时,扫描app下得指定文件(stark1.py)并执行
        def ready(self):
            autodiscover_modules('stark1')  # 这句的作用就是让django找到我们的stark1。py模块,执行里面的site.register()方法,进行初始化路由。简化版的在这里完成了,已经可以看到页面了,我需要下次的写高级一点的。
    

    中级版本:

    可以将app01.stark1.py改成
    # 自定义配置类
    class BookStark(ModelStark):
        def edit(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            return mark_safe("<a href='/stark/app01/book/%s/change'>编辑</a>" % obj.pk)
    
        def delete(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            return mark_safe("<a href='/stark/app01/book/%s/delete'>删除</a>" % obj.pk)
    
        list_display = ["nid", "title", "price", edit, delete]
    
    # 补充
    field_obj = Book._meta.get_field("title")   #就是一个charField字段的对象
    field_obj.max_length  # 最大长度
    
    starkapp.service.stark.py改成这样,你只需要改着一个函数的内容
     def change_list(self, request):
            header_list = []
            for field in self.list_display:
                if callable(field):
                    val = field(self, is_header=True)
                    header_list.append(val)
                else:
                    field_obj = self.model._meta.get_field(field)
                    header_list.append(field_obj.verbose_name)
            data_list = self.model.objects.all()
            new_data_list = []
            for obj in data_list:
                temp = []
                for field in self.list_display:
                    if callable(field):
                        val = field(self, obj)
                    else:
                        val = getattr(obj, field)
                    temp.append(val)
                new_data_list.append(temp)
            return render(request, "index.html", locals())
    
    index.html改成这样
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
    </head>
    <body>
    <h3>数据展示</h3>
    
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <table class="table table-striped table-hover">
                    <thead>
                        <tr>
                            {% for foo in header_list %}
                            <td>{{ foo }}</td>
                            {% endfor %}
                        </tr>
                    </thead>
                    <tbody>
                        {% for data in new_data_list %}
                            <tr>
                                {% for item in data %}
                                <td>{{ item }}</td>
                                {% endfor %}
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    </body>
    </html>
    

    ​ 然后你的页面就变成了下面这一样:接下来你也想到只要在starkapp.service.satrk下的modelstark类中将,那些函数补全你可以删除和编辑了。

    现在我感觉前面缺少选择框:
    # 自定义配置类
    class BookStark(ModelStark):
        def edit(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            return mark_safe("<a href='/stark/app01/book/%s/change'>编辑</a>" % obj.pk)
    
        def delete(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            return mark_safe("<a href='/stark/app01/book/%s/delete'>删除</a>" % obj.pk)
    	
    	# 加了这么一行
        def select(self, obj=None, is_header=None):
            if is_header:
                return mark_safe("<input id='mutPut' type='checkbox'>")
            return mark_safe("<input type='checkbox' value=%s>" % obj.pk)
        list_display = [select, "nid", "title", "price", edit, delete]
    
    然后加上是加上了,但是没有事件: index页面引入jquery,加上下面一句就有了
    <script>
        $("#mutPut").click(function () {
            if ($(this).prop("checked")){
                $("tbody [type='checkbox']").prop("checked", true)
            }else{
                $("tbody [type='checkbox']").prop("checked", false)
            }
        })
    </script>
    
    # 但是现在我感觉,编辑删除以及选择的函数中的href中的url都是写死的,其他的模型使用不了,所以我们是用反向解析,这样就可以适用所有的模型了,同时为了每个模型都需要这三个方法,所以将这三个方法移入modelstark中。
     # 展示编辑连接
        def edit_link(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            name = "{}_{}_change".format(self.app_label, self.model_name)
            return mark_safe("<a href=%s>编辑</a>" % reverse(name, args=(obj.pk,)))
    
        # 原本都是在用户自己的配置类中写,但是现在写入modelStark中是因为,是因为每个表可能都能用到,减少代码重复
        def delete_link(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            name = "{}_{}_delete".format(self.app_label, self.model_name)
            return mark_safe("<a href=%s>删除</a>" % reverse(name, args=(obj.pk,)))
    
        def select_btn(self, obj=None, is_header=None):
            if is_header:
                return mark_safe("<input id='mutPut' type='checkbox'>")
            return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk)
        
        # 组成新的list_display
        @property
        def get_list_display(self):
            new_list_display = []
            new_list_display.extend(self.list_display)
            if not self.list_display_link:
                new_list_display.append(ModelStark.edit_link)
            new_list_display.append(ModelStark.delete_link)
            new_list_display.insert(0, ModelStark.select_btn)
    
            return new_list_display
    
        # 相应的下面的路由也需要加上别名,我们是用app_name和mdoel——name,以及要执行的操作三者放一起组成每个模型唯一的别名
        def get_urls_2(self):
            temp = []
            model_name = self.model._meta.model_name  # 当前模型表
            app_label = self.model._meta.app_label  # 当前app
    
            temp.append(url(r"^add/$", self.add, name="%s_%s_add" % (app_label, model_name)))
            temp.append(url(r"^(\d+)/delete/$", self.delete, name="%s_%s_delete" % (app_label, model_name)))
            temp.append(url(r"^(\d+)/change/$", self.change, name="%s_%s_change" % (app_label, model_name)))
            temp.append(url(r"^$", self.change_list, name="%s_%s_list" % (app_label, model_name)))
            return temp
        
        def change_list(self, request):
            中list_display = self.get_list_display()  记得改
    
    # django中的admin有list_display我们已经实现了,现在我们来实现list_display_link
    BookStrak中加一句
    class BookStark(ModelStark):
        list_display = ["nid", "title", "price"]
        list_display_link = ["nid", "title"]
    
    # 之后修改modelStark中的    
    @property
    def get_list_display(self):
        new_list_display = []
        new_list_display.extend(self.list_display)
        # 如果设置了list_display_link就把编辑删除
        if not self.list_display_link:
            new_list_display.append(ModelStark.edit_link)
            new_list_display.append(ModelStark.delete_link)
            new_list_display.insert(0, ModelStark.select_btn)
    
            return new_list_display
        
    # 最后将chang_list中的获取数据的代码改动一下
    data_list = self.data_list
            new_data_list = []
            # 这里才是我们数据库中的数据
            for obj in data_list:
                temp = []
                for field in self.config.get_list_display:
                    if callable(field):
                        val = field(self.config, obj)
                    else:
                        val = getattr(obj, field)
                        # 如果有list_display_link,就把他编程a标签
                        if field in self.config.list_display_link:
                            val = mark_safe("<a href=%s>%s</a>" % (self.config.get_edit_url(obj), val))
                    temp.append(val)
                new_data_list.append(temp)
    

    add:

    # 发现admin还有添加数据的操作,我们也需要实现,使用modelForm
    # 其他的组件我们慢慢实现
    - ModelStark中加一个函数
        # 使用modelform组件
        def get_modelfrom_class(self):
            class ModelFormClass(ModelForm):
                class Meta:
                    model = self.model
                    fields = "__all__"
            if not self.get_modelfrom_class:
                return ModelFormClass
            else:
                return self.model_form_class
    - 完善add添加函数的逻辑
        def add(self, request):
            form = self.model_form_class
            if request.method == "GET":
                return render(request, "add_index.html", locals())
            else:
                data = request.POST
                form = form(data=data)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, "add_index.html", locals())
    
    # add_index.html页面
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
        <script type="application/javascript" src="/static/jquery.js"></script>
        <style>
            .form-group input{
                display: block;
                 100%;
                height: 34px;
                padding: 6px 12px;
                font-size: 14px;
                line-height: 1.42857143;
                color: #555;
                background-color: #fff;
                background-image: none;
                border: 1px solid #ccc;
                border-radius: 4px;
                -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
                box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
                -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
                -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
                transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
            }
            .error{
                        color: red;
                    }
        </style>
    </head>
    <body>
    <h3>添加数据</h3>
    {% include "add_conponent.html" %}
    </body>
    </html>
    
    # 使用模板循环使用
    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            <label for="">{{ field.label }}:</label>
                            <div>
                                {{ field }}
                                <span class="error pull-right">
                                    {{ field.errors.0 }}
                                </span>
                            </div>
                        </div>
                    {% endfor %}
                <p><input type="submit" class="btn btn-primary"></p>
                </form>
            </div>
        </div>
    </div>
    
    
    ### delete&chanage:
    # 添加页面写完了,下来再我们继续完成编辑和删除页面的逻辑,视图中的函数modelStark中
        def get_delete_url(self, obj):
            delete_url = "{}_{}_delete".format(self.app_label, self.model_name)
            return delete_url
    
        def get_add_url(self):
            add_url = "{}_{}_add".format(self.app_label, self.model_name)
            return add_url
    
        def get_list_url(self):
            list_url = "{}_{}_list".format(self.app_label, self.model_name)
            return list_url
        # 上面三个就是为了获取反向解析的name名称
        def delete(self, request, id):
            del_obj = self.model.objects.filter(nid=id).first()
            if request.method == "GET":
                list_url = self.get_list_url()
                # 为什么要确认页面,是因为重要数据需要二次确认,防止误删
                return render(request, "del_index.html", locals())
            else:
                del_obj.delete()
                return redirect(self.get_list_url())
    
        # 编辑页面
        def change(self, request, id):
            form = self.get_modelfrom_class()
            obj = self.model.objects.filter(pk=id).first()
            if request.method == "GET":
                form = form(instance=obj)
                return render(request, "change_index.html", locals())
            else:
                form = form(data=request.POST, instance=obj)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, "change_index.html", locals())
    
    # 编辑的html页面代码:
    # 因为代码和添加页面类似,所以讲代码整合,弄一个子组件
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
        <script type="application/javascript" src="/static/jquery.js"></script>
        <style>
            .form-group input{
                display: block;
                 100%;
                height: 34px;
                padding: 6px 12px;
                font-size: 14px;
                line-height: 1.42857143;
                color: #555;
                background-color: #fff;
                background-image: none;
                border: 1px solid #ccc;
                border-radius: 4px;
                -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
                box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
                -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
                -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
                transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
            }
            .error{
                color: red;
            }
        </style>
    </head>
    <body>
    <h3>编辑数据</h3>
    {% include "add_conponent.html" %}
    </body>
    </html>
    
    
    # add_conponent.html
    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            <label for="">{{ field.label }}:</label>
                            <div>
                                {{ field }}
                                <span class="error pull-right">
                                    {{ field.errors.0 }}
                                </span>
                            </div>
                        </div>
                    {% endfor %}
                <p><input type="submit" class="btn btn-primary"></p>
                </form>
            </div>
        </div>
    </div>
    
    # 刪除页面比较简单delete_index.html
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>删除</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
        <script src="/static/jquery-3.2.1.min.js"></script>
    </head>
    <body>
    <h3>删除页面</h3>
    <div>
        <p>{{ del_obj }}</p>
    </div>
    <form action="" method="post">
        {% csrf_token %}
        <input type="submit" value="确认删除" class="btn btn-danger">
        <a href="{% url list_url %}" class="btn btn-primary">取消</a>
    </form>
    </body>
    </html>
    

    分页:

    # 这是自己写的一个分页器类,其中我们有添加了一些参数
    # params self.request.GET信息 主要是为了保存,请求的参数信息,比如你选了一堆的过滤条件,比如:
    # http://localhost:8000/stark/app01/book/page=1&title=django,但是你的分页器中的路径是这样子的 http://localhost:8000/stark/app01/book/page=1,用户的条件title=django没有保留,那么问题来了,用户发现这样我们次都要选择,你这网站不好用我不玩了,所以为了提高用户体验,我们需要保留用户的筛选过滤条件,那么我们就需要加一个参数params
    class Pagination(object):
    
        def __init__(self, current_page, all_count, base_url, params, per_page_num=4, pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param base_url: 分页中显示的URL前缀
            :param pager_count:  最多显示的页码个数
            :param params:  self.request.GET信息
            """
    
            try:
                current_page = int(current_page)
            except Exception as e:
                current_page = 1
    
            if current_page <1:
                current_page = 1
    
            self.current_page = current_page
    
            self.all_count = all_count
            self.per_page_num = per_page_num
            self.base_url = base_url
            import copy
            params = copy.deepcopy(params)
            params._mutable = True
            self.params = params
    
            # 总页码
            all_pager, tmp = divmod(all_count, per_page_num)
            if tmp:
                all_pager += 1
            self.all_pager = all_pager
            self.pager_count = pager_count
            self.pager_count_half = int((pager_count - 1) / 2)
    
        @property
        def start(self):
            return (self.current_page - 1) * self.per_page_num
    
        @property
        def end(self):
            return self.current_page * self.per_page_num
    
        def page_html(self):
            # 如果总页码 < 11个:
            if self.all_pager <= self.pager_count:
                pager_start = 1
                pager_end = self.all_pager + 1
            # 总页码  > 11
            else:
                # 当前页如果<=页面上最多显示11/2个页码
                if self.current_page <= self.pager_count_half:
                    pager_start = 1
                    pager_end = self.pager_count + 1
    
                # 当前页大于5
                else:
                    # 页码翻到最后
                    if (self.current_page + self.pager_count_half) > self.all_pager:
                        pager_end = self.all_pager + 1
                        pager_start = self.all_pager - self.pager_count + 1
                    else:
                        pager_start = self.current_page - self.pager_count_half
                        pager_end = self.current_page + self.pager_count_half + 1
    
            page_html_list = []
            # 这个地方就用到了,每次我们把保留的参数全部拼接进入url中那么就没有问题了							    # self.base_url:http://localhost:8000/stark/app01/book/ 
            # self.params.urlencode(): ?page=1&title=django
            self.params["page"] = 1
            
            first_page = '<li><a href="%s?%s">首页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(first_page)
    
            if self.current_page <= 1:
                prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
            else:
                self.params["page"] = self.current_page - 1
                prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.params.urlencode(),)
    
            page_html_list.append(prev_page)
    
            for i in range(pager_start, pager_end):
                self.params["page"] = i
                if i == self.current_page:
                    temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
                else:
                    temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
                page_html_list.append(temp)
    
            if self.current_page >= self.all_pager:
                next_page = '<li class="disabled"><a href="#">下一页</a></li>'
            else:
                self.params["page"] = self.current_page + 1
                next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(next_page)
            self.params["page"] = self.all_pager
            last_page = '<li><a href="%s?%s">尾页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(last_page)
    
            return ''.join(page_html_list)
    
    

    分页组件直接在starkapp下面建一个util的包放进去就好了,目录结构是这样子的。

    然后再添加分页的时候返现modelstark组建中change_list函数下的代码是在是太多了,而且后面还需要继续添加,我们整合一下。用一个类ChangeList来封装一下,其中的我们主要是需要获取表头和表体的数据,所以将这两个代码各写一个函数。同时我们将分页组件引入。
    class ChangeList(object):
        def __init__(self, config, request, queryset):
            self.config = config
            self.request = request
            self.queryset = queryset
    
            from starkapp.util.paginator import Pagination
            current_page = request.GET.get("page")
            all_count = self.queryset.count()
            base_url = self.request.path_info
            params = self.request.GET
            paginator = Pagination(current_page, all_count, base_url, params)
            data_list = self.queryset[paginator.start: paginator.end]
            self.paginator = paginator
            self.data_list = data_list
    
        def get_header(self):
            header_list = []
            # 这里的代码是处理表头数据的
            for field in self.config.get_list_display:
                if callable(field):
                    val = field(self.config, is_header=True)
                    header_list.append(val)
                else:
                    if field == "__str__":
                        header_list.append(self.config.model._meta.model_name.upper())
                    else:
                        field_obj = self.config.model._meta.get_field(field)
                        header_list.append(field_obj.verbose_name)
            return header_list
    
        def get_body(self):
            data_list = self.data_list
            new_data_list = []
            # 这里才是我们数据库中的数据
            for obj in data_list:
                temp = []
                for field in self.config.get_list_display:
                    if callable(field):
                        val = field(self.config, obj)
                    else:
                        val = getattr(obj, field)
                        # 如果有list_display_link,就把他编程a标签
                        if field in self.config.list_display_link:
                            val = mark_safe("<a href=%s>%s</a>" % (reverse(self.config.get_edit_url(obj), args=(obj.pk,)), val))
                    temp.append(val)
                new_data_list.append(temp)
            return new_data_list
    
    # modelstark类中的change_list函数进行简化, 这下是不是很简洁了,而且解耦也做好了
    # 首页展示页面
        def change_list(self, request):
            self.request = request
            queryset = self.model.objects.all()
            # 至于为什么将self传过去,是因为changeList类中使用到了
            cl = ChangeList(self, request, queryset)
            return render(request, "index.html", locals())
    
    
    # modelstark中添加
    @property
    def get_search_condition(self):
        from django.db.models import Q
        search_condition = Q()
        search_condition.connector = "or"  # 设置关系为或
        if self.search_fields:  # ["title", "price"]
            key_word = self.request.GET.get("q", None)
            # 如果有值才添加,没有纸就直接返回空的Q()
            self.key_word = key_word
            if key_word:
                for search_field in self.search_fields:
                    # 因为条件设置得是or所以这里才可以成立,如果是and,全部遍历加进去查询可能会出错
                    search_condition.children.append((search_field + "__contains", key_word))
      return search_condition
        
    
    # change_list改
    # 首页展示页面
        def change_list(self, request):
            self.request = request
            add_url = self.get_add_url()
            # search模糊查询
            queryset = self.model.objects.filter(self.get_search_condition)
            # filter模糊查询
            queryset = queryset.filter(self.get_filter_condition)
            cl = ChangeList(self, request, queryset)
            return render(request, "index.html", locals())
    
    # index.html改
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
        <script type="application/javascript" src="/static/jquery.js"></script>
    </head>
    <body>
    <h3>数据展示</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <!-添加-->
                <a href="{% url add_url %}"><button class="btn btn-primary">添加</button></a>
                <!-搜索-->
                {% if cl.config.search_fields %}
                    <div class="pull-right form-group">
                        <form action="" method="get" class="form-inline">
                            <input type="text" class="form-control" name="q" value="{{ cl.config.key_word }}">
                            <input type="submit" class="btn btn-primary" value="search">
                        </form>
                    </div>
                {% endif %}
                <!-主题内容-->
                <form action="" method="post">
                    {% csrf_token %}
                    <!-批量操作下拉框-->
                    <div>
                        <select class="form-control" name="action" id="" style=" 200px;
                        margin: 5px 0; display: inline-block; vertical-align: -1px">
                            <option value="">------------</option>
                            {% for item in cl.handler_action %}
                                <option value="{{ item.name }}">{{ item.desc }}</option>
                            {% endfor %}
                        </select>
                        <button type="submit" class="btn btn-primary">Go</button>
                    </div>
    
                    <!-展示-->
                    <table class="table table-striped table-hover">
                        <thead>
                            <tr>
                                {% for foo in cl.get_header %}
                                <td>{{ foo }}</td>
                                {% endfor %}
                            </tr>
                        </thead>
                        <tbody>
                            {% for data in cl.get_body %}
                                <tr>
                                    {% for item in data %}
                                    <td>{{ item }}</td>
                                    {% endfor %}
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </form>
            <!-分页-->
            <nav aria-label="Page navigation" class="pull-right">
                <ul class="pagination">
                    {{ cl.paginator.page_html|safe }}
                </ul>
            </nav>
            </div>
        </div>
    </div>
    <script>
        $("#mutPut").click(function () {
            if ($(this).prop("checked")){
                $("tbody [type='checkbox']").prop("checked", true)
            }else{
                $("tbody [type='checkbox']").prop("checked", false)
            }
        })
    </script>
    </body>
    </html>
    

    actions:

    # modelstark中添加
    @property
    def get_filter_condition(self):
            from django.db.models import Q
            filter_condition = Q()
            for field, val in self.request.GET.items():
                if field in self.list_filter:
                    filter_condition.children.append((field, val))
            return filter_condition
    # 修改  加上name属性
        def select_btn(self, obj=None, is_header=None):
            if is_header:
                return mark_safe("<input id='mutPut' type='checkbox'>")
            return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk)
    # 更改change_list函数,前段页面就是上面的呢个
    # 首页展示页面
        def change_list(self, request):
            if request.method == "POST":
                func_name = request.POST.get("action")
                # getlist多个值处理
                pk_list = request.POST.getlist("_selected_action")
                queryset = self.model.objects.filter(pk__in=pk_list)
                func = getattr(self, func_name)
                func(queryset=queryset)
            self.request = request
            add_url = self.get_add_url()
            # search模糊查询
            queryset = self.model.objects.filter(self.get_search_condition)
            # filter模糊查询
            queryset = queryset.filter(self.get_filter_condition)
            cl = ChangeList(self, request, queryset)
            return render(request, "index.html", locals())
    # modelstark类中加上
    def get_actions(self):
            temp = []
            temp.extend(self.actions)
            temp.append(ModelStark.patch_delete)
        return temp
    
    # changeList类中加上
    class ChangeList(object):
        def __init__(self, config, request, queryset):
            self.config = config
            self.request = request
            self.queryset = queryset
    
            from starkapp.util.paginator import Pagination
            current_page = request.GET.get("page")
            all_count = self.queryset.count()
            base_url = self.request.path_info
            params = self.request.GET
            paginator = Pagination(current_page, all_count, base_url, params)
            data_list = self.queryset[paginator.start: paginator.end]
            self.paginator = paginator
            self.data_list = data_list
            # actions 批量操作的动作
            self.actions = self.config.get_actions()  # 这里的获取actions是modelstark类中的
        def handler_action(self):
            temp = []
            for action in self.actions:
                temp.append({"name": action.__name__, "desc": action.desc if getattr(action, "desc", None) else action.__name__})
            return temp
    
    
    ### filter: 难
    补充必看:
    # self.config.model._meta.get_field(filter_field_name)给你看一下拿的是什么吧(下面第一张图),可以看到拿到的关联的对多字段的实例,其中我的django是2.0版本以上的,你可以通过related_model拿到对应的模型,之后通过模型管理器拿到对应的数据。(2.0版本已一下的试一试rel.to.objects.all(),这个我也没试,拿不到自己搜一搜吧。)
    filter_field_obj = self.config.model._meta.get_field(filter_field_name)
    # filter_field = FilterField(filter_field_name, filter_field_obj, self)
    print(type(filter_field_obj))
    print(filter_field_obj.related_model.objects.all())
    from django.db.models.fields.related import ManyToManyField
    from django.db.models.fields.related import ForeignKey
    
    # 更爱changeList类
    class ChangeList(object):
        def __init__(self, config, request, queryset):
            self.config = config
            self.request = request
            self.queryset = queryset
    
            from starkapp.util.paginator import Pagination
            current_page = request.GET.get("page")
            all_count = self.queryset.count()
            base_url = self.request.path_info
            params = self.request.GET
            paginator = Pagination(current_page, all_count, base_url, params)
            data_list = self.queryset[paginator.start: paginator.end]
            self.paginator = paginator
            self.data_list = data_list
            # actions 批量操作的动作
            self.actions = self.config.get_actions()
            # filter  过滤的字段
            self.list_filter = self.config.list_filter
    
        def get_filter_link_tag(self):
            link_list = {}
            data = self.request.GET
            import copy
            params = copy.deepcopy(data)
            for filter_field_name in self.config.list_filter:
                # 为什么放里面而不是放外面
                data = self.request.GET
                import copy
                params = copy.deepcopy(data)
    
                current_id = self.request.GET.get(filter_field_name, 0)
                filter_field_obj = self.config.model._meta.get_field(filter_field_name)
                # filter_field = FilterField(filter_field_name, filter_field_obj, self)
                if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
                    data_list = filter_field_obj.related_model.objects.all()
                else:
                    data_list = self.config.model.objects.values_list("pk", filter_field_name)
                temp = []
                # 处理全部标签
                if params.get(filter_field_name, None):
                    del params[filter_field_name]
                    temp.append("<a href='?%s'>全部</a>" % (params.urlencode()))
                else:
                    temp.append("<a class='active' href='?%s'>全部</a>" % (params.urlencode()))
                # 处理数据标签
                for obj in data_list:
                    if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
                        pk, text = obj.pk, str(obj)
                        params[filter_field_name] = pk
                    else:
                        pk, text = obj
                        params[filter_field_name] = text
                    _url = params.urlencode()
                    if current_id == str(pk) or current_id == text:
                        link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                    else:
                        link_tag = "<a href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                    temp.append(link_tag)
                link_list[filter_field_name] = temp
            # 在下面我自己封装的类,可以自己玩玩,只需要将这个函数中的代码全部注释,保留这两句就可以了
            # ff = FilterField(self.config, self.request)
            # link_list = ff.get_filter_link()
            return link_list
    
    # 由于发现上面的呢个函数是在太长,所以我自己试试能不能将它分成一个类,可以练习一下,其实我们写代码的时候当然是都写在一个函数中看起来方便,但是为了装逼吧,所以将每个功能分开,让每个函数变得简单一些,个人感觉这种方式其实不方便阅读代码。
    # 为每一个过滤的字段封装成整体类
    class FilterField(object):
        def __init__(self, config, request):
            self.config = config
            self.request = request
    
        def get_data(self):
            if isinstance(self.filter_field_obj, ForeignKey) or isinstance(self.filter_field_obj, ManyToManyField):
                return self.filter_field_obj.related_model.objects.all()
            elif self.filter_field_obj.choices:
                return self.filter_field_obj.choices
            else:
                return self.config.model.objects.values_list("pk", self.filter_field_name)
    
        def get_params(self):
            data = self.request.GET
            import copy
            params = copy.deepcopy(data)
            return params
    
        def get_filter_link(self):
            link_list = {}
            for filter_field_name in self.config.list_filter:
                self.filter_field_name = filter_field_name
                # 为什么放里面而不是放外面
                params = self.get_params()
                current_id = self.get_current_id()
                self.get_filter_field_obj()
                temp = self.get_link_list(params, current_id)
                link_list[filter_field_name] = temp
            return link_list
    
        def get_current_id(self):
            current_id = self.request.GET.get(self.filter_field_name, 0)
            return current_id
    
        def get_filter_field_obj(self):
            filter_field_obj = self.config.model._meta.get_field(self.filter_field_name)
            self.filter_field_obj = filter_field_obj
    
        def get_link_list(self, params, current_id):
            data_list = self.get_data()
            temp = []
            temp = self.deal_all_tag(params, temp)
            temp = self.deal_data_tag(params, data_list, current_id, temp)
            return temp
    
        def deal_data_tag(self, params, data_list, current_id, temp):
            for obj in data_list:
                pk, text, params = self.get_pk_text(params, obj)
                _url = params.urlencode()
                if current_id == str(pk) or current_id == text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                temp.append(link_tag)
            return temp
    
        def get_pk_text(self, params, obj):
            if isinstance(self.filter_field_obj, ManyToManyField) or isinstance(self.filter_field_obj, ForeignKey):
                pk, text = obj.pk, str(obj)
                params[self.filter_field_name] = pk
            else:
                pk, text = obj
                params[self.filter_field_name] = text
            return pk, text, params
    
        def deal_all_tag(self, params, temp):
            if params.get(self.filter_field_name, None):
                del params[self.filter_field_name]
                temp.append("<a href='?%s'>全部</a>" % (params.urlencode()))
            else:
                temp.append("<a class='active' href='?%s'>全部</a>" % (params.urlencode()))
            return temp
    

    pop页面:

    1.如果在一对多和多对多后面显示加号
    2. +对应跳转路由
    3. 保存记录的同时,将原页面中的下拉菜单中天骄新纪录
    知识点:
    window.opener当前窗口的打开窗口
    
    最后一个也就是下面这幅图中的+号的实现,点击加号直接跳到创建页面,其中的难点就是上面列出的三条,我们一个一个解决。
    
    # 1.如何在一对多和多对多后面显示加号(我们发先这个是在添加页面的时候才显示的所以我们找到add函数),跳到add_index.html页面发现。所有的字段都是通过form组件渲染出来的,那么有没有什么解决的办法。先来一个简易版的直接加。
    
    ```python # 这个时候发现,每个后面都有咋办,我们只想在多对所以及一对多字段添加,这个时候我想到了model,但是发现好想联系不上。然后突然想到使用form组件渲染的,我们打印一下每个字段的类型,发现是一个boundField类型。进入看看 ```
    它有这么多的属性,我们一个一个打印试试,最后发现field就是我们的字段。
    
    ``` 这个时候操作性就来了,我们可以判断这个字段是不是,一对多的类型(多对多继承一对多),然后给他加一个属性。 ```
        # modelstark的add函数中
        def add(self, request):
            modelform = self.get_modelfrom_class()
            from django.forms.boundfield import BoundField
            form = modelform()
            for field in form:
                print(type(field.field))
                if isinstance(field.field, ModelChoiceField):
                    field.is_pop = True
                    related_model_name = field.field.queryset.model._meta.model_name
                    related_app_name = field.field.queryset.model._meta.app_label
                    _url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
                           "?pop_res_id=id_{}".format(field.name)
                    field.url = _url
            if request.method == "GET":
                return render(request, "add_index.html", locals())
            else:
                data = request.POST
                form = modelform(data=data)
                if form.is_valid():
                    obj = form.save()
                    pop_res_id = request.GET.get("pop_res_id")
                    if pop_res_id:
                        res = {"pk": obj.pk, "text": str(obj), "pop_res_id": pop_res_id}
                        return render(request, "pop.html", {"res": res})
                    else:
                        return redirect(self.get_list_url())
                else:
                    return render(request, "add_index.html", locals())
    
    
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-xs-6 col-md-offset-3">
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group">
                            <label for="">{{ field.label }}:</label>
                            <div style="position: relative">
                                {{ field }}
                                <span class="error pull-right">
                                    {{ field.errors.0 }}
                                </span>
                                {% if field.is_pop %}  # 然后这里加上判断就可以了
                                    <a onclick="pop('{{ field.url }}')" style="color: blue; font-size: 30px; position: absolute;top: -10px;">+</a>
                                {% endif %}
                            </div>
                        </div>
                    {% endfor %}
                <p><input type="submit" class="btn btn-primary"></p>
                </form>
            </div>
        </div>
    </div>
    
    2. +对应跳转路由
    加号对应的跳转路由我们可以直接看django自带的admin路由是什么样?
    http://localhost:8000/stark/app01/publish/add/?pop_res_id=id_publish但是是动态的所以我们想第一步中。给每个外键属性的字段加上一个url
             for field in form:
                print(type(field.field))
                if isinstance(field.field, ModelChoiceField):
                    field.is_pop = True
                    related_model_name = field.field.queryset.model._meta.model_name
                    related_app_name = field.field.queryset.model._meta.app_label
                    # 就是这句
                    _url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
                           "?pop_res_id=id_{}".format(field.name)
                    field.url = _url
    这个时候还要解决的时候打开一个新页面
    添加到添加页面的组件中,也就是下面的图。
    <script>
        function pop(url) {
            window.open(url,"","width=600,height=400,top=100,left=200")
        }
    </script>
    
    但是又如何做到,在点击保存之后关闭页面的呢。看到下面红框中的判断没有,返回了一个pop.html页面。
    
    3. 保存记录的同时,将原页面中的下拉菜单中添加新纪录
    # pop.html页面就这几句代码,加注释
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body> 
    <script>
    	# 这句话是将,数据传入打开它的父window,pop_response是父window的一个函数,自己定义就好了。
        window.opener.pop_response('{{ res.pk }}', '{{ res.text }}', '{{ res.pop_res_id }}');
        # 他才是关闭窗口,直接在js中将窗口关闭
        window.close()
    </script>
    </body>
    </html>
    
    # add_index.html如果处理呢?看到里面的函数你就明白了,就是生成一个option标签,绑定数据和属性给,插入下拉框中。
    

    然后到这里就完了。不习惯使用git的但是又怕代码没地方保存哎?

    地址:https://github.com/maxhope8/stark.git(我直接把项目代码弄上去了,但是外面的目录没传上去)

  • 相关阅读:
    日志分析zz
    通过一个非法的指针或者NULL指针调用成员函数会发生什么?
    Android应用程序进程启动过程的源代码分析
    Android应用程序消息处理机制(Looper、Handler)分析
    Android应用程序绑定服务(bindService)的过程源代码分析
    Android系统进程Zygote启动过程的源代码分析
    Android应用程序注册广播接收器(registerReceiver)的过程分析
    Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
    Android系统默认Home应用程序(Launcher)的启动过程源代码分析
    Android应用程序安装过程源代码分析
  • 原文地址:https://www.cnblogs.com/liuzhanghao/p/12083893.html
Copyright © 2011-2022 走看看