• 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
  • 相关阅读:
    PMP:9.项目资源管理
    @JsonIgnore忽略JSON字段
    xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
    android加载不到.so文件
    报错Failed to install the following SDK components: platforms;android-29 Android SDK Platform 29
    Mac 终端启动运行Redis
    Mac 命令行执行Sublime
    Bean转为Json指定字段名,防止被修改大小写
    Rest接口入参忽略大小写 使用jackson注解
    mongo批量导入
  • 原文地址:https://www.cnblogs.com/wcx666/p/10426981.html
Copyright © 2020-2023  润新知