• 10.18正式开发stark组件*(三)


    2018-10-18 19:15:54

    等这个stark组件做完了再上传到github上面,然后再整理博客!这就到周末啦!

    因为models导入的时候出现bug,所以只有源码没有测试数据!

    源码都有注释,已经很详细啦! 一步一步能看懂!

    里面重要的思想就是 用类封装,组件用模块封装! 然后解耦!把重复的东西封装成类!面向对象编程!

    看源码也许是一种享受!

    越努力,与幸运!永远不要高估自己!

    新增了 搜索框!和批量操作!

    批量操作没有默认的删除可以迭代自行完成!批量操作需要用户自己定制函数

    先放上用到的新的知识点!

    # Q 查询的两种方式
    # Book.objects.filter(Q(title="yuan")|Q(price=123))
    #
    # 第二种方式可以传入字符串
    # q=Q()
    # q.connection="or"
    # q.children.append(("title","yuan"))
    # q.children.append(("price",123))
    
    # 模糊查找
    #         ret=self.model.objects.filter(title__startswith="py")
    #         ret=self.model.objects.filter(price__in=[12,34,56,78,222])
    #         ret=self.model.objects.filter(price__range=[10,100])
    #         ret=self.model.objects.filter(title__contains="o")
    #         ret=self.model.objects.filter(title__icontains="o")
    #         print(ret)


    分页的组件(只要传好参数就可以直接用)

    utils/page.py

    import copy
    
    
    # 自定义分页组件   注意参数
    class Pagination(object):
        def __init__(self, current_page, all_count, base_url, params, per_page_num=8, pager_count=11, ):
            """
            封装分页相关数据
            :param params: 接收url里面的参数也就是键值对?xx=2&xx=3&xx=4  类似于这样的
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param base_url: 分页中显示的URL前缀
            :param pager_count:  最多显示的页码个数
            """
    
            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
            # 总页码
            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)
            params = copy.deepcopy(params)
            params._mutable = True
            self.params = params  # self.params : {"page":77,"title":"python","nid":1}
    
        @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-1)/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_start = self.all_pager - self.pager_count + 1
                        pager_end = self.all_pager + 1
                    else:
                        pager_start = self.current_page - self.pager_count_half
                        pager_end = self.current_page + self.pager_count_half + 1
    
            page_html_list = []
            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":77,"title":"python","nid":1}
                # 既保留每次循环的page 有保留了后面参数的值
                # urlencode()是内置方法 把键值拼成url   ?page=1&xx=22 类似于这样的
                # 这样做法仅仅变页码page 然后后面参数也就是条件不变
                self.params["page"] = i  # {"page":72,"title":"python","nid":1}
                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)
    
    
    
    
    
    # class Pagination(object):
    #
    #     def __init__(self, data_num, current_page, url_prefix,params, per_page=10, max_show=4):
    #         """
    #         进行初始化.
    #         :param data_num: 数据总数
    #         :param current_page: 当前页
    #         :param url_prefix: 生成的页码的链接前缀
    #         :param per_page: 每页显示多少条数据
    #         :param max_show: 页面最多显示多少个页码
    #         """
    #         self.data_num = data_num
    #         self.per_page = per_page
    #         self.max_show = max_show
    #         self.url_prefix = url_prefix
    #
    #         # 把页码数算出来
    #         self.page_num, more = divmod(data_num, per_page)
    #         if more:
    #             self.page_num += 1
    #
    #         try:
    #             self.current_page = int(current_page)
    #         except Exception as e:
    #             self.current_page = 1
    #             # 如果URL传过来的页码数是负数
    #         if self.current_page <= 0:
    #             self.current_page = 1
    #             # 如果URL传过来的页码数超过了最大页码数
    #         elif self.current_page > self.page_num:
    #             self.current_page = self.page_num  # 默认展示最后一页
    #
    #         # 页码数的一半 算出来
    #         self.half_show = max_show // 2
    #
    #         # 页码最左边显示多少
    #         if self.current_page - self.half_show <= 1:
    #             self.page_start = 1
    #             self.page_end = self.max_show
    #         elif self.current_page + self.half_show >= self.page_num:  # 如果右边越界
    #             self.page_end = self.page_num
    #             self.page_start = self.page_num - self.max_show
    #         else:
    #             self.page_start = self.current_page - self.half_show
    #             # 页码最右边显示
    #             self.page_end = self.current_page + self.half_show
    #
    #
    #         import copy
    #         self.params=copy.deepcopy(params) # {"page":"12","title_startwith":"py","id__gt":"5"}
    #
    #
    #
    #     @property
    #     def start(self):
    #         # 数据从哪儿开始切
    #         return (self.current_page - 1) * self.per_page
    #
    #     @property
    #     def end(self):
    #         # 数据切片切到哪儿
    #         return self.current_page * self.per_page
    #
    #     def page_html(self):
    #         # 生成页码
    #         l = []
    #         # 加一个首页
    #         l.append('<li><a href="{}?page=1">首页</a></li>'.format(self.url_prefix))
    #         # 加一个上一页
    #         if self.current_page == 1:
    #             l.append('<li class="disabled" ><a href="#">«</a></li>'.format(self.current_page))
    #         else:
    #             l.append('<li><a href="{}?page={}">«</a></li>'.format(self.url_prefix, self.current_page - 1))
    #
    #
    #
    #         # {"page":"12","title_startwith":"py","id__gt":"5"}  #  "page=12&title_startwith=py&id__gt=5"
    #
    #
    #         print(self.params.urlencode())
    #         for i in range(self.page_start, self.page_end + 1):
    #             self.params["page"]=i #  # {"page":"7","title_startwith":"py","id__gt":"5"}  #  "page=7&title_startwith=py&id__gt=5"
    #             if i == self.current_page:
    #                 tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i)
    #             else:
    #                 tmp = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.url_prefix, self.params.urlencode(),i)
    #             l.append(tmp)
    #
    #
    #
    #
    #
    #
    #
    #         # 加一个下一页
    #         if self.current_page == self.page_num:
    #             l.append('<li class="disabled"><a href="#">»</a></li>'.format(self.current_page))
    #         else:
    #             l.append('<li><a href="{}?page={}">»</a></li>'.format(self.url_prefix, self.current_page + 1))
    #         # 加一个尾页
    #         l.append('<li><a href="{}?page={}">尾页</a></li>'.format(self.url_prefix, self.page_num))
    #         return "".join(l)

    app01/models.py

    from django.db import models
    
    # Create your models here.
    
    
    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    # Create your models here.
    
    
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        age=models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
    
        def __str__(self):
            return self.name
    
    class AuthorDetail(models.Model):
    
        nid = models.AutoField(primary_key=True)
        birthday=models.DateField()
        telephone=models.BigIntegerField()
        addr=models.CharField( max_length=64)
    
        def __str__(self):
            return self.telephone
    
    
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()
        def __str__(self):
            return self.name
    
    
    class Book(models.Model):
    
        nid = models.AutoField(primary_key=True)
        title = models.CharField( max_length=32)
        publishDate=models.DateField()
        price=models.DecimalField(max_digits=5,decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors=models.ManyToManyField(to='Author',)
        def __str__(self):
            return self.title

    server/server.py

    from django.conf.urls import url
    from django.shortcuts import render, redirect
    from django.urls import reverse
    from django.utils.safestring import mark_safe
    from django.forms import ModelForm
    from stark.utils.page import Pagination
    from django.db.models import Q
    
    
    class ShowList(object):
        # 这是一个配置类的对象初始化
        def __init__(self, config, data_list, request):
            self.config = config
            self.data_list = data_list
            self.request = request
            # 分页
            data_count = self.data_list.count()
            current_page = int(self.request.GET.get("page", 1))
            base_path = self.request.path
            self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=3, pager_count=11,)
            self.page_data = self.data_list[self.pagination.start:self.pagination.end]
            # actions   获取actions这个配置类的列表
            self.actions = self.config.actions  # [patch_init,]
    
        # 获取下拉框 用户配置的action_list
        def get_action_list(self):
            temp = []
            for action in self.actions:
                #  [{"name":""patch_init,"desc":"批量初始化"}]
               temp.append({
                   "name": action.__name__,
                   "desc": action.short_description
               })
            return temp
    
        # 构建表头
        def get_header(self):
            header_list = []
            print("header", self.config.new_list_play())
            # [checkbox,"pk","name","age",edit ,deletes]     【checkbox ,"__str__", edit ,deletes】
            for field in self.config.new_list_play():
    
                if callable(field):
                    # header_list.append(field.__name__)
                    val = field(self.config, header=True)
                    header_list.append(val)
    
                else:
                    if field == "__str__":
                        header_list.append(self.config.model._meta.model_name.upper())
                    else:
                        # header_list.append(field)
                        val = self.config.model._meta.get_field(field).verbose_name
                        header_list.append(val)
            return header_list
    
        # 构建表单数据
        def get_body(self):
            new_data_list = []
            for obj in self.page_data:
                temp = []
                for filed in self.config.new_list_play():  # ["__str__",]      ["pk","name","age",edit]
                    if callable(filed):
                        val = filed(self.config, obj)
                    else:
                        val = getattr(obj, filed)
                        if filed in self.config.list_display_links:
                            # "app01/userinfo/(d+)/change"
                            _url = self.config.get_change_url(obj)
    
                            val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
                    temp.append(val)
                new_data_list.append(temp)
            return new_data_list
    
    
    class ModelStark(object):
        # 默认的list_play[]
        list_display = ["__str__", ]
        list_display_links = []
        modelform_class = None
        search_fields = []
        actions = []
    
        def __init__(self, model, site):
            self.model = model
            self.site = site
    
        # 配置表头: 删除 编辑,复选框
        def edit(self, obj=None, header=False):
            """编辑"""
            if header:
                return "操作"
            # return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
            _url = self.get_change_url(obj)
            return mark_safe("<a href='%s'>编辑</a>" % _url)
    
        def deletes(self, obj=None, header=False):
            """删除"""
            if header:
                return "操作"
            # return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
            _url = self.get_delete_url(obj)
            return mark_safe("<a href='%s'>删除</a>" % _url)
    
        def checkbox(self, obj=None, header=False):
            """复选框"""
            if header:
                return mark_safe('<input id="choice" type="checkbox">')
            # value的值不能写死,
            return mark_safe('<input class="choice_item" type="checkbox" name="selected_pk" value="%s">' % obj.pk)
    
        # 获取配置类的表头信息
        def get_modelform_class(self):
            """获取表的配置类"""
            if not self.modelform_class:
                # 如果表的配置类为空
                class ModelFormDemo(ModelForm):
                    class Meta:
                        model = self.model
                        fields = "__all__"
                        labels = {
                            ""
                        }
                return ModelFormDemo
            else:
                return self.modelform_class
    
        # 添加的视图函数
        def add_view(self, request):
            ModelFormDemo = self.get_modelform_class()
            if request.method == "POST":
                form = ModelFormDemo(request.POST)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
    
                return render(request, "add_view.html", locals())
    
            form = ModelFormDemo()
    
            return render(request, "add_view.html", locals())
    
        # 删除的视图函数
        def delete_view(self, request, id):
            url = self.get_list_url()
            if request.method == "POST":
                self.model.objects.filter(pk=id).delete()
                return redirect(url)
            return render(request, "delete_view.html", locals())
    
        # 编辑的视图函数
        def change_view(self, request, id):
            ModelFormDemo = self.get_modelform_class()
            edit_obj = self.model.objects.filter(pk=id).first()
            if request.method == "POST":
                form = ModelFormDemo(request.POST, instance=edit_obj)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                return render(request, "add_view.html", locals())
            form = ModelFormDemo(instance=edit_obj)
            return render(request, "change_view.html", locals())
    
        # 搜索的视图函数
        def get_serach_conditon(self, request):
            key_word = request.GET.get("q", "")
            self.key_word = key_word
            search_connection = Q()
            if key_word:
                # self.search_fields # ["title","price"]
                search_connection.connector = "or"
                # 用Q的这种添加方法可以添加字符串
                for search_field in self.search_fields:
                    # search_field+"__contains"  ---->  title__contains="o"   就是title字段里面包含字母o的
                    search_connection.children.append((search_field + "__contains", key_word))
            return search_connection
    
        # 查看的视图函数
        def list_view(self, request):
            if request.method == "POST":  # action
                print("POST:", request.POST)
                action = request.POST.get("action")
                selected_pk = request.POST.getlist("selected_pk")
                # 通过getattr()传入函数名的字符串和调用该函数的对象 拿到该函数的变量
                action_func = getattr(self, action)
                queryset = self.model.objects.filter(pk__in=selected_pk)
                # 把request和queryset对象传给函数,然后执行,最后return给用户配置的那个函数
                ret = action_func(request, queryset)
                return ret
            # 获取search的Q对象
            search_connection = self.get_serach_conditon(request)
            # 筛选获取当前表所有数据
            data_list = self.model.objects.all().filter(search_connection)     # 【obj1,obj2,....】
            # 按这ShowList展示页面
            showlist=ShowList(self, data_list, request)
            # 构建一个查看URL
            add_url = self.get_add_url()
            return render(request, "list_view.html", locals())
    
        #  获取用户配置类里面的list_play[]
        def new_list_play(self):
            temp = []
            temp.append(ModelStark.checkbox)
            temp.extend(self.list_display)
            if not self.list_display_links:
                temp.append(ModelStark.edit)
            temp.append(ModelStark.deletes)
            return temp
    
        """把url进行反向解析,解耦到各自的函数中,函数中直接返回了对应的url"""
        # 获取修改页面的url
        def get_change_url(self, obj):
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
    
            return _url
    
        # 获删除改页面的url
        def get_delete_url(self, obj):
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
    
            return _url
    
        # 获取添加页面的url
        def get_add_url(self):
    
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_add" % (app_label, model_name))
    
            return _url
    
        # 获取查看页面的url
        def get_list_url(self):
    
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_list" % (app_label, model_name))
    
            return _url
    
        # 二级url分发函数
        def get_urls_2(self):
    
            temp = []
    
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            temp.append(url(r"^add/", self.add_view, name="%s_%s_add" % (app_label, model_name)))
            temp.append(url(r"^(d+)/delete/", self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
            temp.append(url(r"^(d+)/change/", self.change_view, name="%s_%s_change" % (app_label, model_name)))
            temp.append(url(r"^$", self.list_view, name="%s_%s_list" % (app_label, model_name)))
    
            return temp
    
        @property
        def urls_2(self):
            print(self.model)
            return self.get_urls_2(), None, None
    
    
    class StarkSite(object):
        def __init__(self):
            self._registry = {}
    
        def register(self, model, stark_class=None):
            if not stark_class:
                stark_class = ModelStark
    
            self._registry[model] = stark_class(model, self)
    
        # 一级分发url函数
        def get_urls(self):
            temp = []
            for model, stark_class_obj in self._registry.items():
                model_name = model._meta.model_name
                app_label = model._meta.app_label
                # 分发增删改查
                temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2))
    
                '''
                url(r"^app01/userinfo/",UserConfig(Userinfo).urls_2),
                url(r"^app01/book/",ModelStark(Book).urls_2), 
    
    
                '''
            return temp
    
        @property
        def urls(self):
    
            return self.get_urls(), None, None
    
    
    # 创建stark的一个单例对象
    site = StarkSite()

    list.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
        <script src="/static/js/jquery-1.12.4.min.js"></script>
    </head>
    <body>
    
    <h4>数据列表</h4>
    
    
    <div class="container">
        <div class="row">
            <div class="col-md-9">
                <a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
    
                {% if showlist.config.search_fields %}
                         <form action="" class="pull-right">
                            <input type="text" name="q" value="{{ showlist.config.key_word }}"><button>submit</button>
                        </form>
                {% endif %}
    
                <form action="" method="post">
                        {% csrf_token %}
                        <select name="action" id="" style=" 200px;padding: 5px 8px;display: inline-block">
                            {% for item in showlist.get_action_list %}
                                <option value="">---------------</option>
                                <option value="{{ item.name }}">{{ item.desc }}</option>
                            {% endfor %}
    
                        </select><button type="submit" class="btn btn-info">Go</button>
                        <table class="table table-bordered table-striped">
                            <thead>
                                 <tr>
                                     {% for item in showlist.get_header %}
                                       <th>{{ item }}</th>
                                     {% endfor %}
    
    
                                 </tr>
    
                            </thead>
                            <tbody>
                                 {% for data in showlist.get_body %}
    
                                  <tr>
                                      {% for item in data %}
                                       <td>{{ item }}</td>
                                      {% endfor %}
    
                                  </tr>
                                 {% endfor %}
    
                            </tbody>
                        </table>
                            <nav class="pull-right">
                                <ul class="pagination">
                                   {{ showlist.pagination.page_html|safe }}
                                </ul>
                            </nav>
    
                </form>
            </div>
        </div>
    </div>
    
    
    
    <script>
    
       $("#choice").click(function () {
    
         if($(this).prop("checked")){
               $(".choice_item").prop("checked",true)
         }else {
               $(".choice_item").prop("checked",false)
         }
    
       })
    
    </script>
    </body>
    </html>

    app01/server.py

    from stark.service.stark import site, ModelStark
    from django.shortcuts import HttpResponse
    from .models import *
    from django.forms import ModelForm
    
    
    class BookModelForm(ModelForm):
        class Meta:
            model = Book
            fields = "__all__"
            labels = {
                "title": "书籍名称",
                "price": "价格"
            }
    
    
    # Book表的配置类
    class BookConfig(ModelStark):
        # 自定义显示字段
        list_display = ["title", "price", "publishDate"]
        modelform_class = BookModelForm
        # 自定义搜索字段
        search_fields = ["title", "price"]
    
        # 自定义action函数,在下拉框中显示
        def patch_init(self, request, queryset):
            print(queryset, request)
            queryset.update(price=123)
            return HttpResponse("批量初始化OK")
    
        patch_init.short_description = "批量初始化"
        actions = [patch_init]
    
    
    site.register(Book,BookConfig)
    site.register(Publish)
    site.register(Author)
    site.register(AuthorDetail)

    这个stark完完全仿照的admin !并且仿照的很成功!

  • 相关阅读:
    vue 项目界面绘制_stylus_iconfont_swiper
    react_结合 redux
    BOM 浏览器对象模型_当前窗口的浏览历史 history 对象
    BOM 浏览器对象模型_Storage 接口
    react_app 项目开发 (9)_数据可视化 ECharts
    react_app 项目开发 (8)_角色管理_用户管理----权限管理 ---- shouldComponentUpdate
    BOM 浏览器对象模型_同源限制
    面试题: 多个 await 处理,有一个失败,就算作失败
    react_app 项目开发 (7)_难点集合
    react_app 项目开发_遇到的坑
  • 原文地址:https://www.cnblogs.com/zhen1996/p/9812632.html
Copyright © 2020-2023  润新知