stark组件

主要内容:

  • 1. 动态生成增删改查路由
  • 2. 查 --  changelist_view

1. 动态生成增删改查路由:

 #在StarkConfig类中  
  def wrapper(self, func):
        '''
        一个装饰器
        '''
        @functools.wraps(func)
        def inner(request,*args, **kwargs):
            #每次请求进来都获取一份
            self.request = request
            return func(request,*args, **kwargs)
        return inner

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            # 使用装饰器的另一种写法,同时使用命名路由
            url(r'list/$', self.wrapper(self.changelist_view),name='%s_%s_changelist' % info),
            url(r'add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            url(r'change/(?P<pk>d+)', self.wrapper(self.change_view),name='%s_%s_change' % info),
            url(r'del/(?P<pk>d+)', self.wrapper(self.delete_view),name='%s_%s_delete' % info),
        ]
        extra = self.extra_url()
        if extra:
            urlpatterns.extend(extra)
        return urlpatterns

    def extra_url(self):
        '''
        为单独的某张表添加额外的url
        '''
        pass

   @property
    def urls(self):
        #再上一级的路由分发是StarkConfig对象 或者 它的派生类调用改静态方法
        return self.get_urls()
动态生成增删改查路由

2.  查 --  changelist_view

   #List的视图函数函数 
    def changelist_view(self,request):
        '''
        所有url的查看列表页面
        '''
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法请求')
            response = getattr(self,action_name)(request)
            if response:
                return response

        ## 处理搜索 ##
        search_list, q, con = self.get_search_condition(request)
        # 通过model_class(app01.models.UserInfo)
        #filter 中可以 加以逗号分隔的字段 ,也可以放 **{} , 或者放q对象

        # ##### 处理分页 #####
        from stark.utils.pagination import Pagination
        total_count = self.model_class.objects.filter(con).count()
        query_params = request.GET.copy()
        query_params._mutable = True
        page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]

        cl = ChangeList(self, queryset, q, search_list, page)
        context = {
            'cl': cl
        }
        return render(request, 'stark/changelist.html', context)C
changelist_view

ChangeList,封装列表页面需要的所有数据

class ChangeList(object):
    '''
    封装列表页面需要的所有数据(功能)
    '''
    def __init__(self,config,queryset,q,search_list,page):
        self.q = q
        self.search_list = search_list
        self.page = page
        self.config = config
        self.action_list = [{'name': func.__name__, 'text': func.text} for func in config.get_action_list()]
        self.add_btn = config.get_add_btn() 
        self.queryset = queryset
        self.list_display = config.get_list_display()
ChangeList

2.1 定制表格显示列 -- (使用inclusion_tag)

  • 视图中代码片段
    #代码片段
    cl = ChangeList(self, queryset, q, search_list, page)
            context = {
                'cl': cl
            }
    
     #在ChangeList 类中
    self.list_display = config.get_list_display()
    
    #在StarkConfig 类中
         def get_list_display(self):
            '''
            相比于直接引用self.list_display 可以自定制显示内容
            '''
            return self.list_displaytemplatetags  -- stark.py
    视图中代码片段
  • templatetags  -- stark.py
    from django.template import Library
    from types import FunctionType
    
    register = Library()
    def header_list(cl):
        """
        表头
        :param cl:
        :return:
        """
        if cl.list_display:
            for name_or_func in cl.list_display:
                if isinstance(name_or_func, FunctionType):
                    verbose_name = name_or_func(cl.config, header=True)
                else:
                    verbose_name = cl.config.model_class._meta.get_field(name_or_func).verbose_name
                yield verbose_name
        else:
            yield cl.config.model_class._meta.model_name
    
    def body_list(cl):
        """
        表格内容
        :param cl:
        :return:
        """
        for row in cl.queryset:
            row_list = []
            if not cl.list_display:
                row_list.append(row)
                yield row_list
                continue
            for name_or_func in cl.list_display:
                if isinstance(name_or_func, FunctionType):
                    val = name_or_func(cl.config, row=row)
                else:
                    val = getattr(row, name_or_func)
                row_list.append(val)
            yield row_list
    
    @register.inclusion_tag('stark/table.html')
    def table(cl):
    
        return {'header_list':header_list(cl),'body_list':body_list(cl)}
    templates-tags 中代码文件
  • 返回的html代码
    <table class="table table-bordered">
        <thead>
        <tr>
            {% for item in header_list %}
                <th>{{ item }}</th>
            {% endfor %}
        </tr>
        </thead>
        <tbody>
        {% for row_list in body_list %}
            <tr>
                {% for col in row_list %}
                    <td>{{ col }}</td>
                {% endfor %}
    
            </tr>
        {% endfor %}
        </tbody>
    </table>
    html代码片段
    注:在ChangeListView 返回的html页面   导入;  {% load stark %}  使用;  {% table cl %}

2.2 List 页面中的功能 - 提交的action 功能 (批量初始化,批量删除等)

  • ListView中通过  action_dict = self.get_action_dict() 获取到action_dic
    # action_list中存放的是执行action行为的方法名
        action_list = [multi_init, StarkConfig.multi_delete]
        def get_action_list(self):
            val = []
            val.extend(self.action_list)
            return val
    
        def get_action_dict(self):
            val = {}
            for item in self.action_list:
                #将函数名str => k ,函数本身做value
                val[item.__name__] = item
            return val
    View Code
  • ListView 中 通过反射获取到具体的方法名 gettattr 并执行
      # 以批量删除为例
       def multi_delete(self, request):
            """
            批量删除的action
            :param request:
            :return:
            """
            pk_list = request.POST.getlist('pk')
            self.model_class.objects.filter(pk__in=pk_list).delete()
            # return HttpResponse('删除成功')
    View Code
  • template的html页面中
    <form class="form-inline" method="post">
                {% csrf_token %}
                {% if cl.action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in cl.action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {% table cl %}
                <nav aria-label="Page navigation">
                    <ul class="pagination">
                        {{ cl.page.page_html|safe }}
                    </ul>
                </nav>
            </form>
    View Code

    注:在 StarkConfig类中或在为每张表写的 StarkConfig的派生类中的 静态字段action_list中存放的是对应具体操作的函数名 

2.3  在List页面中,表格中数据前checkbox的定制

  • 添加checkbox功能的方法:
        def display_checkbok(self,row=None,header = False):
            '''
            添加checkbox的功能
            '''
            if header:
                return '选择'
            return mark_safe("<input type='checkbox' name='pk' value='%s'/>" % row.pk)
    View Code

    注: 从后端返回标签或者html代码,需要使用  mark_safe 

2.4 LIst页面编辑 & 删除按钮

  • 添加edit 和编辑按钮的方法
        def display_edit(self,row=None,header = False):
            '''
            添加编辑功能
            '''
            if header:
                return '编辑'
            return mark_safe('<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>'% self.reverse_edit_url(row))
    
        def display_del(self,row=None,header = False):
            '''
            添加删除功能
            '''
            if header:
                return '删除'
            return mark_safe('<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>'% self.reverse_del_url(row) )
    
        def display_edit_del(self, row=None, header=False):
            if header:
                return "操作"
            tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
            <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
            """ % (self.reverse_edit_url(row),self.reverse_del_url(row))
            return mark_safe(tpl)
    View Code
  • 关于删除和编辑的a标签路由 在后边编辑和删除操作返回List页面描述

2.5 List页面的添加按钮

  • add按钮的类方法
     def get_add_btn(self):
            return mark_safe('<a href="%s" class="btn btn-info">添加</a>'% self.reverse_add_url())
    View Code
  • a标签跳转链接
        def reverse_list_url(self):
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_changelist'%(namespace,app_label,model_name)
            list_url = reverse(name)
        
            #跳转到list页面前的url
            origin_condition = self.request.GET.get(self.back_condition_key)
    
            if not origin_condition:
                return list_url
    
            list_url = "%s?%s" %(list_url,origin_condition)
            return list_url
    View Code

2.6 关键字搜索

  • 搜索执行的方法,以及search_list定制的字段
        
        # search_list为字段列表  
        def get_search_list(self):
            val = []
            val.extend(self.search_list)
            return val
    
        def get_search_condition(self,request):
            search_list = self.get_search_list()
            q = request.GET.get('q',"")
            con = Q()
            con.connector = "OR"
            if q:
                for field in search_list:
                    con.children.append(('%s__contains' % field,q))
            return search_list,q,con
    View Code
  • 在changelist视图函数中
    ## 处理搜索 ##
            search_list, q, con = self.get_search_condition(request)
    View Code
  • 使用搜索条件后从数据库中查到的queryset的需要加入 Q()作为filter的条件

注: 技术点 : q 对象的使用

 2.7 编辑&删除, 或者再使用分页,模糊查询后,在进行编辑和删除后,  保留搜索条件

  • 编辑和删除按钮中a标签的url
        def reverse_edit_url(self, row):
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_change'%(namespace,app_label,model_name)
            edit_url = reverse(name,kwargs={'pk':row.pk})
    
            if not self.request.GET:
                return edit_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
    
            edit_url = "%s?%s" %(edit_url,new_query_dict.urlencode(),)
    
            return edit_url
    
        def reverse_del_url(self, row):
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_delete' % (namespace, app_label,model_name)
            del_url = reverse(name, kwargs={'pk': row.pk})
    
            if not self.request.GET:
                return del_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
    
            del_url = "%s?%s" %(del_url,new_query_dict.urlencode(),)
    
            return del_url
    View Code
  • 在此时的reverse_url中跳转到List页面的    reverse_list_url 
        def reverse_list_url(self):
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_changelist'%(namespace,app_label,model_name)
            list_url = reverse(name)
    
            #跳转到list页面前的url
            origin_condition = self.request.GET.get(self.back_condition_key)
    
            if not origin_condition:
                return list_url
    
            list_url = "%s?%s" %(list_url,origin_condition)
            return list_url
    View Code

注: 技术点:QueryDict的使用

2.8 分页组件 

  • 项目中创建utils文件夹,创建一个  pagination.py
    """
    分页组件
    """
    from urllib.parse import urlencode
    
    class Pagination(object):
        def __init__(self, current_page, all_count, base_url,query_params, per_page=10, pager_page_count=11):
            """
            分页初始化
            :param current_page: 当前页码
            :param per_page: 每页显示数据条数
            :param all_count: 数据库中总条数
            :param base_url: 基础URL
            :param query_params: QueryDict对象,内部含所有当前URL的原条件
            :param pager_page_count: 页面上最多显示的页码数量
            """
            self.base_url = base_url
            try:
                self.current_page = int(current_page)
                if self.current_page <= 0:
                    raise Exception()
            except Exception as e:
                self.current_page = 1
            self.query_params = query_params
            self.per_page = per_page
            self.all_count = all_count
            self.pager_page_count = pager_page_count
            pager_count, b = divmod(all_count, per_page)
            if b != 0:
                pager_count += 1
            self.pager_count = pager_count
    
            half_pager_page_count = int(pager_page_count / 2)
            self.half_pager_page_count = half_pager_page_count
    
        @property
        def start(self):
            """
            数据获取值起始索引
            :return:
            """
            return (self.current_page - 1) * self.per_page
    
        @property
        def end(self):
            """
            数据获取值结束索引
            :return:
            """
            return self.current_page * self.per_page
    
        def page_html(self):
            """
            生成HTML页码
            :return:
            """
            # 如果数据总页码pager_count<11 pager_page_count
            if self.pager_count < self.pager_page_count:
                pager_start = 1
                pager_end = self.pager_count
            else:
                # 数据页码已经超过11
                # 判断: 如果当前页 <= 5 half_pager_page_count
                if self.current_page <= self.half_pager_page_count:
                    pager_start = 1
                    pager_end = self.pager_page_count
                else:
                    # 如果: 当前页+5 > 总页码
                    if (self.current_page + self.half_pager_page_count) > self.pager_count:
                        pager_end = self.pager_count
                        pager_start = self.pager_count - self.pager_page_count + 1
                    else:
                        pager_start = self.current_page - self.half_pager_page_count
                        pager_end = self.current_page + self.half_pager_page_count
    
            page_list = []
    
            if self.current_page <= 1:
                prev = '<li><a href="#">上一页</a></li>'
            else:
                self.query_params['page'] = self.current_page - 1
                prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url,self.query_params.urlencode())
            page_list.append(prev)
            for i in range(pager_start, pager_end + 1):
                self.query_params['page'] = i
                if self.current_page == i:
                    tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
                        self.base_url, self.query_params.urlencode(), i,)
                else:
                    tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
                page_list.append(tpl)
    
            if self.current_page >= self.pager_count:
                nex = '<li><a href="#">下一页</a></li>'
            else:
                self.query_params['page'] = self.current_page + 1
                nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
            page_list.append(nex)
            page_str = "".join(page_list)
            return page_str
    分页代码
  • changelist view中处理分页部分的代码
    # ##### 处理分页 #####
            from stark.utils.pagination import Pagination
            total_count = self.model_class.objects.filter(con).count()
            query_params = request.GET.copy()
            query_params._mutable = True
            page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]
    View Code

3.  增 --  add_view

  • add_view的视图函数
        def add_view(self,request):
            '''
            所有的添加页面都在此函数进行处理
            '''
            AddModelForm = self.get_model_form_class()
            if request.method == "GET":
                form = AddModelForm()
                return render(request,'stark/change.html',{'form':form})
    
            form = AddModelForm(request.POST)
            if form.is_valid():
                form.save()
                return redirect(self.reverse_list_url())
            return render(request,'stark/change.html',{'from':form})
    View Code
  • 获取form
        def get_model_form_class(self):
            if self.model_form_class:
                return self.model_form_class
    
            class AddModelForm(forms.ModelForm):
                class Meta:
                    model = self.model_class
                    fields = "__all__"
            return AddModelForm
    View Code

注: form的获取是通过 类方法 get_model_form_class,对于不同情况可以自己在StarkConfig的派生类中自定制

4.  改--  edit_view

  • edit_view的视图函数
        def change_view(self,request,pk):
            '''
            所有的编辑页面
            '''
            obj = self.model_class.objects.filter(pk=pk).first()
            if not obj:
                return HttpResponse('数据不存在')
    
            ModelFormClass = self.get_model_form_class()
            if request.method == "GET":
                form = ModelFormClass()
                return render(request,'stark/change.html',{'form':form})
            form = ModelFormClass(data=request.POST,instance=obj)
            if form.is_valid():
                form.save()
                return redirect(self.reverse_list_url())
            return render(request, 'stark/change.html', {'form': form})
    View Code
  • 关于instance 

5. 删-- delete_view

  • del_view的视图函数
      def delete_view(self,request,pk):
            '''
            所有删除页面
            '''
            if request.method =='GET':
                return render(request,'stark/delete.html',{'cancel_url':self.reverse_list_url()})
    
            self.model_class.objects.filter(pk=pk).delete()
            return redirect(self.reverse_list_url())
    View Code
原文地址:https://www.cnblogs.com/wcx666/p/10426981.html