zoukankan      html  css  js  c++  java
  • stark

    stark组件

    介绍:

    ​ stark组件,是一个帮助开发者快速实现数据库表的增删改查。

    目标:

    ​ 10s 钟完成一张表的增删改查

    前戏

    项目启动前加载指定文件
    • Django 启动时,在执行 url.py之前执行一个指定程序

      # 在已注册的APP中 app.py 里面
      # django 内部有两个线程在运行,一个是运行Django,一个是检测代码是否发生变化
      #runserver   --norload  不启动检测线程
      class appconfig(appconfig):
          name = 'app'
          def ready(self):
              # 路由加载之前 在已注册的所有APP中自动寻找xxxx.py文件,并导入
              # 如果执行两次,是因为Django内部自动重启导致
              autodiscover_modules("xxxx")
      
      # 提示:
      	如果xxxx.py 执行的代码向“某个神奇的地方”放入了一些值,之后的路由加载时,可以去'某个神奇的地方'读取原理的值
      
    单例模式
    • 一个实例,一个对象

      # 多实例
      class foo(object):
          pass
      obj1 = foo()
      print(obj1)       #<__main__.foo object at 0x02B9E670>
      obj2 = foo()
      print(obj2)       #<__main__.foo object at 0x02B9E538>   这是两个实例
      
    • 通过python 模块导入的方式,实现单例模式

      xx.py
      class AdminSite(object):
          pass
      
      site = AdminSite() # 为AdminSite类创建了一个对象(实例)
      
      
      app.py
      import utils
      print(utils.site)
      import utils
      print(utils.site)
      
      """
      在Python中,如果已经导入过的文件再次被重新导入时候,python不会再重新解释一遍,而是选择从内存中直接将原来导入的值拿来用。
      """
      
    • 补充

      • 如果以后存在一个单例模式的对象,可以先在此对象中放入一个值,然后在其他的文件中导入该对象,通过对象再次将值获取到

    Dango路由分发的本质,include
    • 方式一

      from django.conf.urls import url,include
      
      urlpatterns = [
      	url(url(r'^web/', include('app01.urls')),   
          # include 返回一个元组 (urlconf_module, app_name, namespace))
      ]
      
    • 方式二

      # include 函数主要返回有三个元素的元组
      from django.conf.urls import url,include
      from app01 import urls
      urlpatterns = [
      	url(r'^web/', (urls, app_name, namespace)),
      ]
      

    开始:

    1.创建 Django project

    2.创建基础业务表

    1. app01/models.py
      部门表
      用户表

    2. app02/models.py
      主机表

    3.对以上的三张表做增删改查

    a.分析
    1. 为每张表创建4个url

    2. 为每张表创建4个视图函数

      # 避免url重复,默认以应用名为开头
      app01/models.py
          Depart
              /app01/depart/list/
              /app01/depart/add/
              /app01/depart/edit/(d+)/
              /app01/depart/del/(d+)/
      
          UserInfo
              /app01/userinfo/list/
              /app01/userinfo/add/
              /app01/userinfo/edit/(d+)/
              /app01/userinfo/del/(d+)/
      app02/models.py
          Host
              /app02/host/list/
              /app02/host/add/
              /app02/host/edit/(d+)/
              /app02/host/del/(d+)/
      
      
    b.为app中的每个model类自动创建URL以及相关视图函数
    1. 将视图提取到基类
      # v1.py
      class StarkSite(object):
          def __init__(self):
              self._registry = []
              self.app_name = 'stark'
              self.namespace = 'stark'
      
          def register(self, model_class, handler_class):
              """
              :param model_class: 是models中的数据库表对应的类。 models.UserInfo
              :param handler_class: 处理请求的视图函数所在的类
              :return:
              """
              self._registry.append({'model_class': model_class, 'handler': handler_class(model_class)})
      
          def get_urls(self):
              patterns = []
              for item in self._registry:
                  model_class = item['model_class']
                  handler = item['handler']
                  app_label, model_name = model_class._meta.app_label, model_class._meta.model_name
                  # model_class._meta.app_label 获取models类所在的app名称
                  # model_class._meta.model_name 获取models类的名称
                  # patterns.append(url(r'x1/',lambda request:HttpResponse("1")),)
                  patterns.append(url(r'%s/%s/list/$' % (app_label, model_name,), handler.changelist_view))
                  patterns.append(url(r'%s/%s/add/$' % (app_label, model_name,), handler.add_view))
                  patterns.append(url(r'%s/%s/change/(d+)/$' % (app_label, model_name,), handler.change_view))
                  patterns.append(url(r'%s/%s/del/(d+)/$' % (app_label, model_name,), handler.change_view))
      
              return patterns
      
          @property
          def urls(self):
              return self.get_urls(), self.app_name, self.namespace
      site = StarkSite()
      
      #url.py
      from django.contrib import admin
      from django.urls import path
      from stark.service.v1 import site
      print(site._registry)
      urlpatterns = [
          path('admin/', admin.site.urls),
          path('^stark/', site.urls),  #(site.get_urls(), site.app_name, site.namespace)
      ]
      
    2. URL分发扩展&后缀

    3. 为url设置别名

    4. url的别名进行重新生成

    c.定制页面显示的列
    1. 基本列表页面的定制

    2. 未定义list_display字段的页面,默认显示对象

      class StarkHandler(object):
          list_display = []
          def changelist_view(self, request):
              if list_display:
                  for i in list_display:
                  pass
              else:
                  pass
      
    3. 为页面显示的列预留一个钩子函数

      class StarkHandler(object):
          list_display = []
          def get_list_display(self):
              value = []
              value.extend(self.list_display)
              return value
          def changelist_view(self, request):
              list_display = self.get_list_display()
              if list_display:
                  for i in list_display:
                  pass
              else:
                  pass
      
    4. 为页面提供自定义显示的函数
      v1.py

      class StarkHandler(object):
              def display_edit(self, obj=None, is_header=None):
              """
              自定义页面显示的列(表头和内容)
              :param obj:
              :param is_header:
              :return:
              """
              if is_header:
                  return "编辑"
              name = "%s:%s" % (self.site.namespace, self.get_change_url_name,)
              return mark_safe('<a href="%s">编辑</a>' % reverse(name, args=(obj.pk,)))
          # 根据名称空间,别名,反向生成url
      
          def display_del(self, obj=None, is_header=None):
              if is_header:
                  return "删除"
              name = "%s:%s" % (self.site.namespace, self.get_delete_url_name,)
              return mark_safe('<a href="%s">删除</a>' % reverse(name, args=(obj.pk,)))
          def changelist_view(self, request):
                      if list_display:
                  for key_or_func in list_display:
                      if isinstance(key_or_func, FunctionType):
      				# key_or_func虽然本来是hander中的方法,
      				#但在传入时是以函数传入的,所以要把self也传入
                          verbose_name = key_or_func(self, obj=None, is_header=True)
                      else:
                          verbose_name = self.model_class._meta.get_field(key_or_func).verbose_name
                      header_list.append(verbose_name)
      

      stark.py

      from stark.service.v1 import site, StarkHandler,get_choice_text
      class UserInfoHandler(StarkHandler):
          list_display = ['name',
                          'age', 'email', 'depart',
                          StarkHandler.display_edit,
                          StarkHandler.display_del]
      site.register(models.UserInfo,UserInfoHandler)
      
    5. 应用

    d.后台应用模板样式
    e.列表页面添加分页的功能
    f.添加按钮
    • 如何显示添加按钮

    • 如何添加按钮的url

      from django.http import QueryDict
      import functools
      class StarkHandler(object):
          list_display = []
          def __init__(self, site, model_class, prev):
              self.site = site
              self.model_class = model_class
              self.prev = prev
              self.request = None
              self.has_add_btn = True # 默认要显示添加按钮
            
          def reverse_add_url(self):
              """
              生成带有原搜索条件的添加URL
              :return:
              """
              name = "%s:%s" % (self.site.namespace, self.get_add_url_name,)
              base_url = reverse(name)
              if not self.request.GET:
                  add_url = base_url
              else:
                  param = self.request.GET.urlencode()
                  new_query_dict = QueryDict(mutable=True)
                  new_query_dict['_filter'] = param
                  add_url = "%s?%s" % (base_url, new_query_dict.urlencode())
              return add_url
          
          def get_add_btn(self):
              # 返回添加按钮,href 通过 reverse_add_url()获得反向生成的url
              if self.has_add_btn:
                  # 判断是否要显示添加按钮
                  return "<a class='btn btn-primary' href='%s'>添加</a>" 
              					% self.reverse_add_url()
              return None
      
          def changelist_view(self, request):
      		add_btn = self.get_add_btn()
              return render(request,'stark/changelist.html',
                  {'add_btn': add_btn,})
      
          def wrapper(self, func):
              @functools.wraps(func)
              # @functools.wraps 保留原函数的参数信息
              def inner(request, *args, **kwargs):
                  # 通过装饰器,使每一个请求进入视图函数后都能保存自己的request
                  self.request = request
                  return func(request, *args, **kwargs)
              return inner
          
          def get_urls(self):
              patterns = [
                  url(r'^list/$', self.wrapper(self.changelist_view), 		                  										name=self.get_list_url_name),
                  url(r'^add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
                  url(r'^change/(?P<pk>d+)/$', self.wrapper(self.change_view), 		                      							name=self.get_change_url_name),
                  url(r'^delete/(?P<pk>d+)/$', self.wrapper(self.delete_view), 	 	           										name=self.get_delete_url_name),
              ]
              patterns.extend(self.extra_urls())
              return patterns
      
      
    • 添加页面进行添加数据
      可以添加显示的字段,接口供用户扩展
      需求 :少显示字段,保存时,减少的字段也得设置,否则存储出错

      from django import forms
      
      class StarkModelForm(forms.ModelForm):
          def __init__(self, *args, **kwargs):
              super(StarkModelForm, self).__init__(*args, **kwargs)
              # 统一给ModelForm生成字段添加样式
              for name, field in self.fields.items():
                  field.widget.attrs['class'] = 'form-control'
      
      class StarkHandler(object):
          
          
          def save(self, form, is_update=False):
              """
              在使用ModelForm保存数据之前预留的钩子方法
              """
              form.save()
              
          model_form_class = None
          def get_model_form_class(self):
              if self.model_form_class:
                  # 如果自定义了model_form_class,及用自定义的,如果没有,用默认的
                  return self.model_form_class
      
              class DynamicModelForm(StarkModelForm):  #继承StarkModelForm,来继承bootstrap样式
                  class Meta:
                      model = self.model_class  # 根据当前的model,来显示
                      fields = "__all__"
      
              return DynamicModelForm
          
          def reverse_list_url(self):
              name = "%s:%s" % (self.site.namespace, self.get_list_url_name,)
              base_url = reverse(name)
              param = self.request.GET.get('_filter')
              if not param:
                  return base_url
              return "%s?%s" % (base_url, param,)  
      
          def add_view(self, request):
              """
              添加页面
              :param request:
              :return:
              """
              model_form_class = self.get_model_form_class()
              if request.method == 'GET':
                  form = model_form_class()
                  return render(request, 'stark/change.html', {'form': form})
              form = model_form_class(data=request.POST)
              # 验证传回的form表单信息
              if form.is_valid():
                  self.save(form, is_update=False)
                  # 在数据库保存成功后,跳转回列表页面(携带原来的参数)。
                  return redirect(self.reverse_list_url())
              return render(request, 'stark/change.html', {'form': form})
      
      

      stark.py

      class UserInfoModelForm(StarkModelForm):
          class Meta:
              model = models.UserInfo
              fields = ['name', 'gender', 'classes', 'age', 'email']
              
      class UserInfoHandler(StarkHandler):
          model_form_class = UserInfoModelForm
          # 自定义要渲染的form表单,由于在显示的时候可以
      site.register(models.UserInfo,UserInfoHandler) 
      
    g.编辑功能实现

    ​ 根据pk值,来展示编辑页面,所有要传入pk
    ​ 看到的东西有默认值

    class StarkHandler(object):
        list_display = []
    
        def display_edit(self, obj=None, is_header=None):
            """
            自定义页面显示的列(表头和内容)
            """
            if is_header:
                return "编辑"
            return mark_safe('<a href="%s">编辑</a>' % self.reverse_change_url(pk=obj.pk))    
        def reverse_change_url(self, *args, **kwargs):
            """
            # 在原基础上, *args, **kwargs 
            生成带有原搜索条件的编辑URL
            :param args:
            :param kwargs:
            :return:
            """
            name = "%s:%s" % (self.site.namespace, self.get_change_url_name,)
            base_url = reverse(name, args=args, kwargs=kwargs)
            if not self.request.GET:
                add_url = base_url
            else:
                param = self.request.GET.urlencode()
                new_query_dict = QueryDict(mutable=True)
                new_query_dict['_filter'] = param
                add_url = "%s?%s" % (base_url, new_query_dict.urlencode())
            return add_url    
        
        # 在最开始的时候要获取到pk值
        def get_urls(self):
            # 正则获取pk值
            patterns = [
                url(r'^list/$', self.wrapper(self.changelist_view),                                   name=self.get_list_url_name),
                url(r'^add/$', self.wrapper(self.add_view), 		                                   name=self.get_add_url_name),
                url(r'^change/(?P<pk>d+)/$', self.wrapper(self.change_view),
                    name=self.get_change_url_name),
                url(r'^delete/(?P<pk>d+)/$', self.wrapper(self.delete_view), name=self.get_delete_url_name),
            ]
    
            patterns.extend(self.extra_urls())
            return patterns
        
        
        def change_view(self, request, pk):
            """
            编辑页面
            :param request:
            :param pk:
            :return:
            """
            current_change_object = self.model_class.objects.filter(pk=pk).first()
            if not current_change_object:
                return HttpResponse('要修改的数据不存在,请重新选择!')
    
            model_form_class = self.get_model_form_class()
            if request.method == 'GET':
                form = model_form_class(instance=current_change_object)
                # 获取当前models的form组件来渲染form表单
                return render(request, 'stark/change.html', {'form': form})
            form = model_form_class(data=request.POST, instance=current_change_object)
            if form.is_valid():
                self.save(form, is_update=False)
                # 在数据库保存成功后,跳转回列表页面(携带原来的参数)。
                return redirect(self.reverse_list_url())
            return render(request, 'stark/change.html', {'form': form})
    
    h.删除
    class StarkHandler(object):    
        def display_del(self, obj=None, is_header=None):
            if is_header:
                return "删除"
            return mark_safe('<a href="%s">删除</a>' % self.reverse_delete_url(pk=obj.pk))    
        def reverse_delete_url(self, *args, **kwargs):
            """
            生成带有原搜索条件的删除URL
            :param args:
            :param kwargs:
            :return:
            """
            name = "%s:%s" % (self.site.namespace, self.get_delete_url_name,)
            base_url = reverse(name, args=args, kwargs=kwargs)
            if not self.request.GET:
                add_url = base_url
            else:
                param = self.request.GET.urlencode()
                new_query_dict = QueryDict(mutable=True)
                new_query_dict['_filter'] = param
                add_url = "%s?%s" % (base_url, new_query_dict.urlencode())
            return add_url
        def delete_view(self, request, pk):
            """
            删除页面
            :param request:
            :param pk:
            :return:
            """
            origin_list_url = self.reverse_list_url()
            if request.method == 'GET':
                return render(request, 'stark/delete.html', {'cancel': origin_list_url})
    
            self.model_class.objects.filter(pk=pk).delete()
            return redirect(origin_list_url)
    
    
    

    4.其它功能

    a.排序
        order_list = []
    
        def get_order_list(self):
            return self.order_list or ['-id', ]
    
        def changelist_view(self, request):
            """
            列表页面
            :param request:
            :return:
            """
    
            # ########## 1. 获取排序 ##########
            order_list = self.get_order_list()
            queryset = self.model_class.objects.all().order_by(*order_list)    
            # 以下的数据都经过 排序,以下都用queryset
    
    b.模糊搜索

    实现思路:
    在页面设置form表单,搜索:以get形式提交到后台,后台获取数据然后进行筛选

    ​ 在后端获取关键字后,根据

    from django.db.models import Q    
        search_list = ['name__contains',] # 如果不加__contains就算精确查找
    
        def get_search_list(self):
            return self.search_list
        def changelist_view(self, request):
            """
            列表页面
            :param request:
            :return:
            """
             # ########## 0. 模糊查询 ##########
            #1,如果search_list 中没有值,则不显示搜索框
            #2.获取用户提交的关键字
            # 3.构造条件
            # Django Q 对象,用户构造复杂的orm查询条件
            search_list = self.get_search_list()
            search_value = request.GET.get('q', '')
            conn = Q()
            conn.connector = 'OR'
            if search_value:
                # 如果用户没有搜索,就不构造
                for item in search_list:
                    conn.children.append((item, search_value))
    
            # ########## 1. 获取排序 ##########
            order_list = self.get_order_list()
            queryset = self.model_class.objects.filter(conn).order_by(*order_list)
      # filter.all()变成了filter(conn)
    
    c.批量操作
    • 添加checkbox列

    • 生成批量操作的按钮

    • 在display_list中添加一列

    • 将函数传入 html,中,函数会自动执行

    • 将提交的函数名,通过反射来找函数执行

    • 扩展,handler的函数预留参数接口 *args, **kwargs

    • 扩展,点击批量操作的执行后,能够跳转某个页面去查看某些内容

      change_listhtml.

      {% if action_dict %}
      // 如果下拉框有值才显示,
          <div style="float: left;margin: 5px 10px 5px 0;">
              <div class="form-inline">
                  <div class="form-group">
                      <select class="form-control" name="action">
                          <option value="">请选择操作</option>
                          {% for func_name,func_text in action_dict.items %}
                              <option value="{{ func_name }}">{{ func_text }}</option>
                          {% endfor %}
                      </select>
                      <input class="btn btn-primary" type="submit" value="执行"/>
                  </div>
              </div>
          </div>
      {% endif %}
      
          def display_checkbox(self, obj=None, is_header=None):
              """
              :param obj:
              :param is_header:
              :return:
              """
              if is_header:
                  return "选择"
              return mark_safe('<input type="checkbox" name="pk" value="%s" />' % obj.pk)
          
          
          action_list = []  #在里面放上函数
          def get_action_list(self):
              return self.action_list
          
          def action_multi_delete(self, request, *args, **kwargs):
              """
              批量删除(如果想要定制执行成功后的返回值,那么就为action函数设置返回值即可。)    return redirect('html')
              :return:
              """
              pk_list = request.POST.getlist('pk')
              self.model_class.objects.filter(id__in=pk_list).delete()
      
          action_multi_delete.text = "批量删除"        
      
          def changelist_view(self, request, *args, **kwargs):
              """
              列表页面
              :param request:
              :return:
              """
              # ########## 1. 处理Action ##########
              action_list = self.get_action_list()
              # 将函数传入 html,中,函数会自动执行,所以将函数改为字典
              action_dict = {func.__name__: func.text for func in action_list}  		  # {'multi_delete':'批量删除','multi_init':'批量初始化'}
      
              if request.method == 'POST':
                  action_func_name = request.POST.get('action')
                  if action_func_name and action_func_name in action_dict:
                      # 用户提交的函数必须是我们定义过的,避免用户修改html代码来破坏
                      action_response = getattr(self, action_func_name)(request, *args, **kwargs)
                      if action_response:
                          # 如果函数有返回值,就证明要跳转到某个页面
                          return action_response
      
      
    d.组合搜索
    • 什么是组合搜索

    • 如何实现组合搜索

      • 实现思路

        • 第一步:根据字段找到其关联的数据,choice,FK,M2M

        • 第二步:根据配置获取关联数据,不写,就不显示

        • 第三步:根据配置获取相关数据(含条件)

        • 第四步:

        • 第五步:为组合

          • 生成url时,不影响其他组影响、

            1. 手动拼接,"/stark/app01/userinfo/list" + '?depart=1'
            2. 根据request.get来生成,每点击一次,都能生成根据这次点击,而形成的url
                        query_dict = self.rquests.copy()
                        query_dict._mutable = True
                        query_dict['depart'] = 1
            
            

        class SearchGroupRow(object):
            def __init__(self, title, queryset_or_tuple, option):
                """
        		# 将querysert,或元组,组装成一个统一的可迭代对象
                :param title: 组合搜索的列名称
                :param queryset_or_tuple: 组合搜索关联获取到的数据
                :param option: 配置
                """
                self.title = title
                self.queryset_or_tuple = queryset_or_tuple
                self.option = option
        
            def __iter__(self):
                yield '<div class="whole">'
                yield self.title
                yield '</div>'
        
                yield '<div class="others">'
                yield "<a>全部</a>"
                for item in self.queryset_or_tuple:
                    text = self.option.get_text(item)
                    yield "<a href='#'>%s</a>" % text 
                    #此处写死,不易扩展,比如要对字符串进行加工,所以取一个函数返回值
        
                yield '</div>'
        
        
        
        class Option(object):
            def __init__(self, field, db_condition=None, text_func=None):
                """
                :param field: 组合搜索关联的字段
                :param db_condition: 数据库关联查询时的条件
                :param text_func: 此函数用于显示组合搜索按钮页面文本
                """
                self.field = field
                if not db_condition:
                    db_condition = {}
                self.db_condition = db_condition
                self.text_func = text_func
        
                self.is_choice = False
        
            def get_db_condition(self, request, *args, **kwargs):
                return self.db_condition
        
            def get_queryset_or_tuple(self, model_class, request, *args, **kwargs):
                """
                根据字段去获取数据库关联的数据
                :return:
                """
                # 根据gender或depart字符串,去自己对应的Model类中找到 字段对象
                field_object = model_class._meta.get_field(self.field)
                title = field_object.verbose_name
                # 获取关联数据
                if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
                    # FK和M2M,应该去获取其关联表中的数据: QuerySet
                    db_condition = self.get_db_condition(request, *args, **kwargs)
                    return SearchGroupRow(title, field_object.rel.model.objects.filter(**db_condition), self)
                else:
                    # 获取choice中的数据:元组
                    self.is_choice = True
                    return SearchGroupRow(title, field_object.choices, self)
        
            def get_text(self, field_object):
                """
                获取文本函数,
                :param field_object:
                :return:
                """
                if self.text_func:
                    return self.text_func(field_object)
        
                if self.is_choice:
                    return field_object[1]
        
                return str(field_object)
        
        
                    
                    
        class StarkHandler(object): 
            search_group = []
            def get_search_group(self):
                return self.search_group
            
            def changelist_view(self, request, *args, **kwargs):
                """
                列表页面
                :param request:
                :return:
                """
                search_group_row_list = []
                search_group = self.get_search_group()  
                # ['gender', 'depart']
                for option_object in search_group:
                    # 根据gender或depart 字符串,去自己对应的model类中找到字段对象
                    option_object.get_queryset_or_tuple(self.model_class, request, *args, **kwargs)    
                    search_group_row_list.append(row)
        

        stark.py

        class UserInfoHandler(StarkHandler):
            search_group = [
                Option('gender'),
                MyOption('depart', {'id__gt': 2}),
            ]
        
        site.register(models.UserInfo, UserInfoHandler)
        

    总结:

    列表页面

    添加页面

    编辑页面

    删除页面

    关键字搜索

    批量操作

    组合搜索

    保留原函数的信息

    后台make_safe,和前端管道符safe效果一样

  • 相关阅读:
    Pagodas
    Bazinga
    取石子问题
    Sudoku Killer(hdu 1426 数独)
    欧拉函数
    CCPC Ancient Go
    ZZNU 1992: 情人节的尴尬
    fzu Problem 2128 最长子串(KMP + strstr 经典好题)
    POJ
    HDU
  • 原文地址:https://www.cnblogs.com/leiyiming/p/12807872.html
Copyright © 2011-2022 走看看