• crm


    喜欢请打赏

    扫描二维码打赏

    支付宝打赏

    CRM备课文档

     

    课程大纲

    Django知识点回顾

    """
    1.django请求生命周期
        http协议:
            四大特性:
                1.基于scoket应用于应用层的协议
                2.基于请求响应的协议
                3.无连接
                4.无状态
        WSGI协议:wsgiref,uwsgi,werkzeug实现模块
        中间件:默认七个中间件,五个可自定义方法,全局访问频率限制,权限限制等
            process_request  请求刚进来时
            process_view    经过url执行视图函数之前
            process_template_response   视图函数中return render时触发
            process_exception       视图函数中报错执行
            process_response    返回相应响应时
            
        urls.py:路由控制器
        MTV:
            views.py:
                视图层:FBV,CBV
                    CBV可张开讲:from django.views import View
                    看一下源码
                    
                返回结果必须是HttpResponse对象
                
            templates.py:
                模板语法:变量相关{{}},逻辑相关{%%}
                
            models.py:
                ORM操作
                
                
    2.django session机制
        request.session仅仅只是产生缓存,真正起作用还是中间件
        
    3.form组件 auth认证 简单提一嘴
    
    4.django settings文件配置
    
    5.前后端传输数据编码格式
        简单前后端传输页面代码搭建
        http传输数据格式书写
        三种常见编码格式总结
            urlencoded
            form-data
            json
        请求体数据格式推导(先瞎猜,再用事实验证)
        请求首行作用ContentType参数作用
        浏览器查看form表单默认提交方式以及组织数据格式
        ajax提交数据(默认提交,json提交>>>前后端分离)
            request.POST无法解析>>>request.body存储原始数据
            
    """

    admin使用

    """
    打开博客园django-admin组件,介绍admin是啥
        django自带的一个后台管理组件,快速的帮我们完成对数据的增删改查操作,我们这会是要学习admin的使用,并模仿它(及参考admin的源码)开发一套我们自己的后台管理工具
        
    新建项目,直接访问,需要超级用户
    博客园拷贝图书馆管理系统表格,数据库迁移并注册到admin中
    后台模型表展示
    
    添加一条数据后先总结url的规律
    模型表路由规律总结
        路由中都有admin是因为它是一个组件
        
        推导另外一个模型表的url规律
        总结:
        admin为每一个注册了的模型表创建增删改查四条url
    
    你们用admin应该只到这一步吧,接下来我们继续往深里看
    
    所有表的展示页面是不是都是一样的,现在我想对某个表设置不一样的展示,咋弄?
    再来五大重点参数介绍
        模型表继承admin.ModelAdmin
        
        list_display >>> 演示这个效果之后 看一点源码 配置类概念>>>阐释配置类概念
            为何要继承ModelAdmin
        
        list_display_links
        search_field
        list_filter
        actions
            讲action时,复习一个前端知识,单选多选getlist()
        
    1.启动源码
        django启动,执行每一个app下的admin.py文件
        原因:autodiscover_module('admin')
    
    单例模式复习
        对照博客
        重点在基于模块的单例模式上
            诠释模块导入重复导入只加载一次的特性pyc地方
            再写一个py文件,写一个函数里面调,然后去另外一个文件调函数
        再来个坑:导类过来,实例化两次,求打印结果?
    
    2.admin注册源码摘取精髓部分
    class AdminSite(object):
        def __init__(self, name='admin'):
            self._registry = {}
        def register(self, model_or_iterable, admin_class=None, **options):
            if not admin_class:
                admin_class = ModelAdmin
            self._registry[model] = admin_class(model, self)
    site=AdminSite()
    # **************************源码**************************
    admin.site.register(Book)
    admin.site.register(Book,BookConfig)
    引出配置类作用,self._registry中数据存放格式
    
    打印_registry里面数据带他们看看  这里慢点讲!!!
    """

    3.url设计

    """
    1.url分发本质
    url(r'^index/', ([],None,None))  # 固定写法元祖里面三个参数
    
    2.一级,二级分发演示
    url(r'^stark/',([],None,None))
    第一次推导
        url(r'^index/',([
                url(r'^test01/',test01),
                url(r'^test02/',test02)
        ],None,None))
    第二次推导
        url(r'^index/',([
            url(r'^test01/',([
                url(r'^test01_1',test01_1), 
                url(r'^test01_2',test01_2),
                url(r'^test01_3',test01_3),
            ],None,None)),
            url(r'^test02/',test02)
        ],None,None))
    
    
    自己生成注册了的模型表的url
    def list_view(request):
        return HttpResponse('list_view')
    def add_view(request):
        return HttpResponse('add_view')
    def edit_view(request,id):
        return HttpResponse('edit_view')
    def delete_view(request,id):
        return HttpResponse('delete_view')
    def get_urls1():
        tmp = [
            url(r'^$',list_view),
            url(r'^add/',add_view),
            url(r'^edit/(d+)/',edit_view),
            url(r'^delete/(d)/',delete_view)
        ]
        return tmp
    def get_urls():
        tmp = []
        for class_model,config_obj in admin.site._registry.items():
            app_label = class_model._meta.app_label
            model_name = class_model._meta.model_name
            tmp.append(
                url(r'%s/%s/'%(app_label,model_name),(get_urls1(),None,None))
            )
        return tmp
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^stark/',(get_urls(),None,None))
    ]
    
    """

    CRM项目开始

    """
    组件也是应用,一个项目有多个应用,一个应用可以用在多个项目里
    
    1.admin启动源码(django配置文件整体梳理>>>注册的app中admin)
    2.autodiscover_modules应用
    3.新建django项目>>>stark应用>>>service文件夹
    4.admin源码摘取 + 配置类
    5.url设计
        研究url(r'^admin/', admin.site.urls)源码
        一级分发
        二级分发(配置类介绍)
    6.总结回顾
        解释一下二级分发那个self到底是谁
        
    7.视图函数中self.model诠释
    8.展示各个模型表数据对象
    
    9.依据list_display详细展示数据对象
        表单展示
        表头展示
            verbose_name介绍 model._meta.get_field('字段名').verbose_name
        小bug调试,表头后端代码筛选__str__特殊情况
            对象反射拿__str__不报错的原因>>>联想到迭代器__next__,嫌麻烦,可以写成next()
            
    10.单张表的自定义数据列 选择 编辑 删除>>>拓展至全局配置
        详细讲
            先用户自定义 再转成全局配置
            获取表头表单内部代码优化
    
    11.选择 编辑 删除标识性参数设置
    
    12.反向解析
    
    13.list_display_links
            自定义编辑列根据list_display_links显隐展示
    """

    form与modelform

    """
    2.form组件介绍
        演示form本质应用
        class TestForm(forms.Form):
            name = forms.CharField(max_length=32)
            email = forms.EmailField()
            age = forms.IntegerField()
            
        python console中直接测试
        from app01 import views
        obj = views.TestForm({'name':'jason','email':'123','age':18})
        obj.is_valid()  # 校验数据
        False
        obj.cleaned_data  # 合格的放入这个里面
        {'name': 'jason', 'age': 18}
        obj.errors  # 不合格的放入errors中
        {'email': ['Enter a valid email address.']}
        
        继续演示几种传参类型
        obj = views.TestForm({'name':'jason','age':18})
        obj = views.TestForm({'name':'jason','email':"123@qq.com",'age':18})
        obj = views.TestForm({'name':'jason','email':"123@qq.com",'age':18,'abc':123})
        
    3.form组件完成
        class Book(models.Model):
            title = models.CharField(max_length=32)
            price = models.DecimalField(max_digits=8,decimal_places=2)
            date = models.DateField()
            publish = models.ForeignKey("Publish")
            authors = models.ManyToManyField("Author")
    
        class Publish(models.Model):
            name = models.CharField(max_length=32)
    
        class Author(models.Model):
            name = models.CharField(max_length=32)
    对上述三张表用form完成增  >>>  编辑功能form组件书写难度
    
    1.新建django项目 自定义模型表
    这里详细演戏图书管理系统的增删改查:
        1.先不借助任何组件,自己手动渲染标签
            这里需要注意的问题有前端input标签一定要写name属性
            后端在更新数据的时候,记得用关键字传参的形式去做
            新增用add(*author) 编辑用set(author)
            
        2.借助form组件
            前端三种渲染方式
            
        3.最后推到出modelform组件
            class BookModelForm(forms.ModelForm):
                class Meta:
                    model = models.Book
                    fields = '__all__'
    
    
    4.modelform介绍
    """
    """
    1.modelform完成CRM项目中模型表的增删改查
        这里为什么定义这个方法,主要是用户如果想针对自己的模型表进行其他的特定操作
        def get_model_form(self):
            if self.model_form_class:
                return self.model_form_class
            from django.forms import ModelForm
            class ModelFormClass(ModelForm):
                class Meta:
                    model = self.model
                    fields = '__all__'
            return ModelFormClass
            
            def add_view(self,request):
                model_form = self.get_model_form()
                if request.method == 'POST':
                    model_form_obj = model_form(request.POST)
                    if model_form_obj.is_valid():
                        model_form_obj.save()
                        return redirect(self.get_reverse_url('list'))
                model_form_obj = model_form()
                return render(request,'stark/add_view.html',locals())
                
            def edit_view(self,request,id):
                model_form = self.get_model_form()
                edit_obj = self.model.objects.filter(pk=id).first()
                if request.method == 'POST':
                    model_form_obj = model_form(request.POST,instance=edit_obj)
                    if model_form_obj.is_valid():
                        model_form_obj.save()
                        return redirect(self.get_reverse_url('list'))
                model_form_obj = model_form(instance=edit_obj)
                return render(request,'stark/edit_view.html',locals())
                
            def delete_view(self,request,id):
                delete_obj = self.model.objects.filter(pk=id).delete()
                return redirect(self.get_reverse_url('list'))
    """

    分页

    """
    1.批量插入数据方法
        传统:效率极低
            def index(request):
                for i in range(100):
                    Book.objects.create(title='book_%s'%i,price=i*i)
                return render(request,'book_list.html')
        现在:
            book_list = []  # 里面方法Book对象
            for i in range(100):
                book = Book(title="book_%s"%i,price=i*i)
                book_list.append(book)
            Book.objects.bulk_create(book_list)
    2.展示批量产生的数据 
    3.分页介绍
        博客园分页代码拷贝,解释
        保存搜索条件介绍(拉勾网展示)
        request.GET.urlencoded()构造get请求携带数据格式
    """

    数据展示类

    """
    1.数据展示全部封装到新的Showlist展示类中
    """

    search功能

    """
    1.bootstrap中找寻前端样式
        css样式   >>>     表单
    2.后端模拟简单版本实现效果
        # search功能  简单推导1
        key_word = request.GET.get('q')
        if key_word:
            for field in self.search_fields:
                queryset = self.model.objects.filter(title__icontains=key_word)
    3.如何解决查询条件关键字 title__icontains拼接?
        Q对象简介
            # python console中演示推导过程1
            from app01 import models
            from django.db.models import Q
            models.Book.objects.filter(title='西游记')
            <QuerySet [<Book: 西游记>]>
            models.Book.objects.filter(Q(title='西游记')|Q(price=399))
            <QuerySet [<Book: 西游记>, <Book: 水浒传>]>
            
            # 推导过程2
            q = Q()
            q.children.append(('title','西游记'))
            models.Book.objects.filter(q)
            <QuerySet [<Book: 西游记>]>
            
            # 推导过程3多个条件
            q = Q()
            q.children.append(('title','西游记'))
            models.Book.objects.filter(q)
            <QuerySet [<Book: 西游记>]>
            q.children.append(('price','399'))
            models.Book.objects.filter(q)
            <QuerySet []>
            
            # 最终推导
            from django.db.models import Q
            q = Q()
            q.children.append(("字段名","可能存在的字段值"))  # 特点在于 直接写的是字符串
            q.connector = "or"  # q添加数据默认是和的关系,可通过该参数改变
            Models.objects.filter(q)  # 以q里面添加的条件筛选相应数据
    4.要想实现模糊查询需要
        q.children.append(("字段名"+"__icontains","可能存在的字段值")) 
    5.search功能实现
    6.search功能封装配置类方法
    7.search功能前端搜索保留搜索条件
        配置类对象添加key_word属性
    8.search功能前端界面显隐
        根据key_word属性控制
    """

    action功能

    """
    1.django admin后台action功能演示
    2.前端样式
        <select class='form-control' name='action'>
            <option>----------------</option>
        </select>
        <input type='submit' class='btn btn-info'>
    3.点击提交按钮需要将checkbox选中数据与select选择框中的数据提交到后端,所以需要保证二者在一个form表单中
        <form action="" class="form-inline" method="post">
            {% csrf_token %}
                <select class="form-control s" name="action">
                <option>---------------------------------------</option>
                    {% for func_dic in show_obj.get_actions %}
                        <option value="{{ func_dic.name }}">{{ func_dic.desc }}</option>
                    {% endfor %}
            </select>
            <input type="submit" class="btn btn-warning" style="">
            <table class="table table-bordered table-striped table
       </form>
    4.actions属性 获取信息展示前端
        tmp = []
        for func in self.config_obj.actions:  # [patch_init,]
            tmp.append({
                "name": func.__name__,
                "desc": func.desc
            })
        return tmp
    5.action逻辑处理
        def list_view(self):
            if request.method == 'POST':
                action = request.POST.get('action')
                pk_list = request.POST.getlist('select_action')
                queryset = self.model.objects.filter(pk__in=pk_list)
                action_func = getattr(self,action)
                action_func(request, queryset)
            ...
            
    """

    filter功能

    """
    1.admin list_filter功能演示,展示的其实是一个个的数据对象
    2.前端页面搭建
        <div class="col-md-3">
            <div class="alert-info text-center">FILTER</div>
        </div>
    3.后端
        分析:
            1.list_filter中只能放外键字段
            2.先循环字段,取出该字段对应的模型表,再取每个字段对应的多组数据
                model._meta.get_field('字段').rel.to >>> 模型表
            3.深拷贝request.GET
                获取get请求参数
                保存搜索条件
            4.all标签渲染
                由于传输数据格式为[{'外键字段':[a,b,c,d...],'外键字段':[e,f,g,h...]}]
                每个外键字段对应的列表中都需要一个all标签,而all标签的作用就是去除其所在字段在url中的键值,也就是说只需要pop掉当前循环的字段即可
        
            def get_list_filter(self):
                tmp_dict = {}
                for field in self.config_obj.list_filter:
                    tmp = []
                    relation_model = self.config_obj.model._meta.get_field(field).rel.to
                    queryset = relation_model.objects.all()
                    import copy
                    params = copy.deepcopy(self.request.GET)
                    click_id = self.request.GET.get(field)
    
                    # all标签
                    params2 = copy.deepcopy(self.request.GET)
                    if field in params2:
                        params2.pop(field)
                        all_link = "<a href='?%s'>All</a>" % params2.urlencode()
                    else:
                        all_link = "<a href=''>All</a>"
                    tmp.append(mark_safe(all_link))
    
                    for data in queryset:
                        params[field] = data.pk
                        _url = params.urlencode()
                        if click_id == str(data.pk):
                            s = "<a href='?%s' class='active'>%s</a>" % (_url, data)
                        else:
                            s = '<a href="?%s">%s</a>' % (_url, data)
                        tmp.append(mark_safe(s))
                    tmp_dict[field] = tmp
                return tmp_dic
    4.前端
        <div class="col-md-3">
            <div class="alert-info text-center">FILTER</div>
            {% for k,v in show_obj.get_list_filter.items %}
                <div class="panel panel-default">
                    <div class="panel-heading">By {{ k|upper }}</div>
                    <div class="panel-body">
                        {% for data in v %}
                            <p>{{ data }}</p>
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
        </div>
    5.数据过滤
        外键字段查询时
            model.objects.filter(publish__id=1)
            model.objects.filter(publish=obj)
            同样也支持
            model.objects.filter(publish=1)  >>> 自动转换成第一种
        所以我们再利用q对象往children里面添加数据时可以直接('publishs',1)
        
    """

    open样式演示

    """
    新建django项目演示open方法
        1.window.open 方法
            window.open('https://www.baidu.com','','height=400,width=800')
        2.填写自己的路由,打开添加页面
        3.填写数据,window.opener调用父类方法
        4.如何将创建出来的数据渲染到父页面>>>后端将数据对象传递到子页面,子页面传到父页面
        5.window.open路由中添加前端标签id,后端接收再传递到子页面,子页面再传到父页面
            后端充当中间商,只做数据传输
    """

    pop功能

    """
    1.admin查看效果
    2.加号渲染(必须是一对多或多对多字段,如何判断?)
        我们后台循环的字段是model字段还是form字段?是form字段,意味着没法通过ForeignKey,ManyToManyField判断,因为是form字段
        后端先打印每个字段的类型发现均是BoundField类型,无法判断
        需要拿字段对象再点field属性才能取到真经
        isinstance可以判断多继承关系
        前端如何根据条件渲染这个加号呢?后端给field字段对象添加is_pop=True属性
    3.前端加号样式通过相对移动调节样式
    
    
    open样式演示完之后再回来
    1.加号事件绑定
        路由问题解决,不同加号对应不同的添加页面>>>字段对象能加is_pop属性,能不能再加一个url属性,到时候直接点拿出来就行了(注意一定要放到form字段属性中)
        
        后端form字段对象如何获取对应的model字段的字段名
            不能用self.model 因为它是当前访问的表的添加页面,而加号对应的另外两个表的添加页面,所以这里需要我们手动反向解析url
            需要获取到加号字段对应的模型表
                form字段.name >>> form字段对应的model字段字段名
                self.model._meta.get_filed(form字段.name).rel.to >>> 表
                _url = reverse("%s_%s_add",xx,yy) 解决添加url
                
        select标签id查找
            谷歌浏览器查看 id_publish,id_authors
            form字段.name 拿到对应model字段的字段名
                _url = _url+"?pop_back_id=id_"+form字段.name
                这是我们手动拼接,还可以自动拼接
                _url = _url +"?pop_back_id="+form字段.auto_id  
        
        后端添加页面不能直接返回pop页面,不然的话我们连正常打开的pop页面也会打不开,所以需要根据url中是否有pop_back_id来判断是正常访问还是加号跳转
        
        pop页面调用父页面传pop_back_id,pk,text
        
        父页面DOM操作添加标签
            新添加的数据需要添加selected属性
        
    """

    Stark组件Bug调整

    """
    数据展示的时候,点分页会报错,因为会把page=x传到了filter中,还有就是search查询中的q=x也会报错,所以在filter功能中加if判断去掉
        if field in ['page','q']
            continue
    还有就是这样写了之后如果在url中手动条件额外不存在的键值还是会报错
                    try:
                        queryset = queryset.filter(q)
                    except BaseException:
                        pass
    """

    Stark组件总结

    """
    从头到尾捋一遍
    """

    权限介绍

    """
    1.什么是权限?
        生活中到处都是权限,比如我们办公室密码门,视频vip,等等...
        再回来我们写的web应用,一个公司里面老板和员工用的同一个web服务,两者能干的事也是各不相同
        再来看Django admin应用,访问模型表的增删改查这是不是也是一种权限
        总的来说就是
    
        who             what            how
    
        哪些人对哪些资源拥有哪些操作的权限,看admin的url
    
        http://127.0.0.1:8000/admin/auth/user/对auth下面的user表数据查看权限
    
        http://127.0.0.1:8000/admin/auth/user/1/change/对auth下面的user表数据的修改权限
    
        http://127.0.0.1:8000/admin/auth/user/1/delete/对auth下面的user表数据的删除权限
    
        http://127.0.0.1:8000/admin/auth/user/add/对auth下面的user表数据的增加权限
    
        权限其实就是一个个的url,用户可以访问的url放在一个大列表里,访问对应url的时候,去列表里查找,如果有就代表可以访问,拥有该权限,没有就是禁止访问,没有权限。
    
    2.定义几张表模拟一下
        class User:
            pass
        class Permission:
            pass
        class user2permission:
            pass
        1.创建俩用户一个拥有查,一个拥有查改
        2.一下子来了十个员工,都拥有增删改查四个权限,那我需要在第三张表里面反复录入
    """

    RBAC(Roel Based Access Control)

    """
    1.介绍
    日常生活中的应用软件怎么区分权限的?普通用户,超级管理员,经理,职员...
    是不是都是基于角色的权限管理,每一个角色对应有一定的权限,用户只需按角色划分,就能有对应的权限
    
    2.表关系
    class User:
        pass
    class Role:
        pass
    class Permission:
        pass
    class user2role:
        pass
    class role2permission:
        pass
    一次性有100个员工,你会发现方案1和方案2差距很大!!!   
    """

    项目搭建

    """
    1.新建django项目,stark组件移植过来
        知识点:
            templates文件查找会先从项目根目录找,其次会去应用下面找,再找不到报错
            static文件亦是如此,注意在拖拽的时候,防止pycharm自动加stark前缀,需要手动去除掉,任然以staic开头
            
    2.创建模型表并注册到stark组件中
        class User(models.Model):
            name = ...
            pwd = ...
            roles = ...
        class Role(model.Model):
            title = ...
            permissions = ...
        class Permission(model.Model):
            title = ...
            url = ...
    3.启动项目 添加数据
        权限表 
            角色和用户的增删改查
        角色表
            销售      查看 添加用户
            讲师      查看
            CEO      用户的增删改查
            管理员     所有权限
        用户表
            kevin   销售+讲师
            egon    CEO
            jason   管理员
    
    权限思路:
        1.创建表,录入数据
        2.登陆验证,获取登陆人的权限
        3.权限校验
    
    5.登陆校验获取登陆人的权限
        简单的前端登陆页面
        后端获取查询校验
            需要慢一点的地方在于如何基于用户对象查询权限信息
                user_obj.roles.all()  >>> 角色对象
                user_obj.roles.all().values('permission__url')
        权限列表构造
        session保存
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            user_obj = models.User.objects.filter(name=username,pwd=password).first()
            if user_obj:
                # 记录登陆状态
                request.session['user'] = username
                # 记录权限列表
                permission_list = []
                res = user_obj.roles.values('permissions__url').distinct()
                for permission in res:
                    permission_list.append(permission.get('permissions__url'))
                request.session['permission_list'] = permission_list
        return render(request,'login.html')
    """

    中间件校验权限

    """
    整体流程:
        1.用户访问白名单
            利用正则
        2.判断用户是否登陆
        3.获取用户权限并进行校验
            利用正则(并需要手动加限制"^$")
            
    注册到settings文件中
    
    讲课推理:
        1.判断用户是否登陆
        2.获取权限校验
            for循环直接判断在不在
            正则校验判断
            # 判断是否有权访问
            import re
            for permission_rex in permission_list:
                permission_rex = '^%s$'%permission_rex
                res = re.search(permission_rex,current_path)
                if res:
                    return None
            return HttpResponse('没有访问权限')
            
        3.用户访问白名单
            放开以admin打头的所有访问权限(正则)
            
            
    """

    前端样式调整

    """
    1.模板中加导航条和侧边栏以及整体样式微调
    """

    权限展示

    """
    1.前端页面需要展示菜单权限,但是并不是所有权限都要展示,也并不是都展示在一个主菜单下(通常只展示查看权限),所以我们应该想个办法区分权限的种类>>>权限表添加code字段标识权限种类
    (首页导航条右侧登陆用户展示,注销按钮)
    2.后端login视图函数中除了之前构建的权限列表外,需要再构建当前登录用户的权限名称,url,权限类型的列表套字典格式方便左侧菜单渲染
                # 菜单权限展示
                permission_menu_list = []
                for permission in permissions:
                    permissions_list.append(permission.get('permissions__url'))
                    if permission.get('permissions__code') == 'list':
                        permission_menu_list.append({
                            'title':permission.get("permissions__title"),
                            'url':permission.get("permissions__url")
                        })
                # 存储菜单权限
                request.session['permission_menu_list'] = permission_menu_list
    """

    CRM关系表

    from django.db import models
    
    # Create your models here.
    class User(models.Model):
        name = models.CharField(max_length=64)
        password = models.CharField(max_length=128)
        roles = models.ManyToManyField(to='Role')
    
        def __str__(self):
            return self.name
    
    class Role(models.Model):
        title = models.CharField(max_length=64)
        permissions = models.ManyToManyField(to='Permission')
    
        def __str__(self):
            return self.title
    
    class Permission(models.Model):
        title = models.CharField(max_length=64)
        url = models.CharField(max_length=128)
        code = models.CharField(max_length=64,default='list')
    
        def __str__(self):
            return self.title
    
    
    # CRM关系表
    class Department(models.Model):
        """
        部门表
        市场部     1000
        销售       1001
    
        """
        title = models.CharField(verbose_name='部门名称', max_length=16)
        code = models.IntegerField(verbose_name='部门编号', unique=True, null=False)
    
        def __str__(self):
            return self.title
    
    
    class UserInfo(models.Model):
        """
        员工表
        """
    
        name = models.CharField(verbose_name='员工姓名', max_length=16)
        email = models.EmailField(verbose_name='邮箱', max_length=64)
        depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code")
        user=models.OneToOneField("User",default=1)
        def __str__(self):
            return self.name
    
    
    class Course(models.Model):
        """
        课程表
        如:
            Linux基础
            Linux架构师
            Python自动化开发精英班
            Python自动化开发架构师班
            Python基础班
            go基础班
        """
        name = models.CharField(verbose_name='课程名称', max_length=32)
    
        def __str__(self):
            return self.name
    
    
    class School(models.Model):
        """
        校区表
        如:
            北京沙河校区
            上海校区
    
        """
        title = models.CharField(verbose_name='校区名称', max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class ClassList(models.Model):
        """
        班级表
        如:
            Python全栈  面授班  5期  10000  2017-11-11  2018-5-11
        """
        school = models.ForeignKey(verbose_name='校区', to='School')
        course = models.ForeignKey(verbose_name='课程名称', to='Course')
        semester = models.IntegerField(verbose_name="班级(期)")
    
    
        price = models.IntegerField(verbose_name="学费")
        start_date = models.DateField(verbose_name="开班日期")
        graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
        memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )
    
        teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo')
        tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo',related_name="class_list")
    
    
        def __str__(self):
            return "{0}({1}期)".format(self.course.name, self.semester)
    
    
    class Customer(models.Model):
        """
        客户表
        """
        qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一')
    
        name = models.CharField(verbose_name='学生姓名', max_length=16)
        gender_choices = ((1, '男'), (2, '女'))
        gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
    
        education_choices = (
            (1, '重点大学'),
            (2, '普通本科'),
            (3, '独立院校'),
            (4, '民办本科'),
            (5, '大专'),
            (6, '民办专科'),
            (7, '高中'),
            (8, '其他')
        )
        education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
        graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
        major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True)
    
        experience_choices = [
            (1, '在校生'),
            (2, '应届毕业'),
            (3, '半年以内'),
            (4, '半年至一年'),
            (5, '一年至三年'),
            (6, '三年至五年'),
            (7, '五年以上'),
        ]
        experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
        work_status_choices = [
            (1, '在职'),
            (2, '无业')
        ]
        work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
                                          null=True)
        company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
        salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True)
    
        source_choices = [
            (1, "qq群"),
            (2, "内部转介绍"),
            (3, "官方网站"),
            (4, "百度推广"),
            (5, "360推广"),
            (6, "搜狗推广"),
            (7, "腾讯课堂"),
            (8, "广点通"),
            (9, "高校宣讲"),
            (10, "渠道代理"),
            (11, "51cto"),
            (12, "智汇推"),
            (13, "网盟"),
            (14, "DSP"),
            (15, "SEO"),
            (16, "其它"),
        ]
        source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
        referral_from = models.ForeignKey(
            'self',
            blank=True,
            null=True,
            verbose_name="转介绍自学员",
            help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
            related_name="internal_referral"
        )
        course = models.ManyToManyField(verbose_name="咨询课程", to="Course")
    
        status_choices = [
            (1, "已报名"),
            (2, "未报名")
        ]
        status = models.IntegerField(
            verbose_name="状态",
            choices=status_choices,
            default=2,
            help_text=u"选择客户此时的状态"
        )
    
        consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter' )
    
        date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
        recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True)
        last_consult_date = models.DateField(verbose_name="最后跟进日期", )
    
        def __str__(self):
            return self.name
    
    
    class ConsultRecord(models.Model):
        """
        客户跟进记录
        """
        customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer')
        consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo')
        date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
        note = models.TextField(verbose_name="跟进内容...")
    
        def __str__(self):
            return self.customer.name + ":" + self.consultant.name
    
    
    class Student(models.Model):
        """
        学生表(已报名)
        """
        customer = models.OneToOneField(verbose_name='客户信息', to='Customer')
        class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True)
    
        emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
        company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
        location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
        position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
        salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
        welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
        date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
        memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)
    
        def __str__(self):
            return self.customer.name
    
    
    class ClassStudyRecord(models.Model):
        """
        上课记录表 (班级记录)
        """
        class_obj = models.ForeignKey(verbose_name="班级", to="ClassList")
        day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")
        teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo')
        date = models.DateField(verbose_name="上课日期", auto_now_add=True)
    
        course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
        course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
        has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
        homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
        homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
        exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)
    
        def __str__(self):
            return "{0} day{1}".format(self.class_obj, self.day_num)
    
    
    class StudentStudyRecord(models.Model):
        '''
        学生学习记录
        '''
        classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord")
        student = models.ForeignKey(verbose_name="学员", to='Student')
    
        record_choices = (('checked', "已签到"),
                          ('vacate', "请假"),
                          ('late', "迟到"),
                          ('noshow', "缺勤"),
                          ('leave_early', "早退"),
                          )
        record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
        score_choices = ((100, 'A+'),
                         (90, 'A'),
                         (85, 'B+'),
                         (80, 'B'),
                         (70, 'B-'),
                         (60, 'C+'),
                         (50, 'C'),
                         (40, 'C-'),
                         (0, ' D'),
                         (-1, 'N/A'),
                         (-100, 'COPY'),
                         (-1000, 'FAIL'),
                         )
        score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
        homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
        note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)
    
        homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
        stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
        date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)
    
        def __str__(self):
            return "{0}-{1}".format(self.classstudyrecord, self.student)
    

    数据录入

    """
    学校
    课程
    部门
    用户信息
    班级
    
    
    按照models.py中模型表的顺序依次添加,添加过程中需要引入的知识点有
    1.添加班级表记录时,授课老师和班主任信息展示需要用到limit_choices_to
        limit_choices_to={'depart_id':170}  >>>     单个部门
        limit_choices_to={'depart_id__in':[120,150]}    >>>     多个部门
    
    2.客户表
        录入数据后,展示客户性别引出get_field_display()展示choice字段对应的注解
            def display_gender(self,is_header=False,obj=None):
                if is_header:
                    return '性别'
                return obj.get_gender_display()
            按照上述方式,只要是有choice字段的都可按照此法展示
    
    3.展示咨询课程
        3.1将咨询课程全部做成a标签的样式,然后提出a标签的需求:
            点哪个a标签就将该a标签对应的客户咨询课程删除
        3.2删除的是某一客户的咨询课程,而我们stark组件只会为每一个模型表创建增删改查四条基本的url,这个问题如何解决?
            先去stark组件源码二级路由分发处演示思路,直接在get_urls()演示
                def get_urls(self):
                    tmp = [
                        url(r'^$',self.list_view,name='%s_%s_list'%(self.app_label,self.model_name)),
                        url(r'^add/',self.add_view,name='%s_%s_add'%(self.app_label,self.model_name)),
                        url(r'^edit/(d+)/',self.edit_view,name='%s_%s_edit'%(self.app_label,self.model_name)),
                        url(r'^delete/(d+)/',self.delete_view,name='%s_%s_delete'%(self.app_label,self.model_name))
                    ]
                    tmp.append(
                        url(r'^.../',删除客户咨询某一课程记录)
                    )
                    return tmp
           这样做的缺陷就是会生成很多条相同的url,如何做到只为某一个模型表配置额外的url?
           (你们想想list_display,actions这些功能是怎么玩的???)
           a标签渲染
            def display_course(self,is_header=False,obj=None):
                if is_header:
                    return '咨询课程'
                course_list =  []
                for course in obj.course.all():
                    course_list.append("<a href='/stark/app01/customer/cancel_course/%s/%s/' style='border:1px solid #369;padding:3px 6px'>%s</a>"%(obj.pk,course.pk,course.name))
                return mark_safe(' '.join(course_list))
                
           自定义扩张url方法
            def extra_url(self):
                tmp = []
                tmp.append(
                url(r'^cancel_course/(d+)/(d+)/',self.cancel_course)
                )
                return tmp
                
            自定义视图函数
            def cancel_course(self,request,customer_id,course_id):
                customer_obj = models.Customer.objects.filter(pk=customer_id).first()
                customer_obj.course.remove(course_id)
                return redirect(self.get_reverse_url('list'))
                
            stark组件新增一个空的extra_url方法,用户不配置就无任何多余的url
            def extra_url(self):
                return []   
    """

    剩余四张表

    """
    1.咨询记录表信息录入
    
    2.学生信息录入
    
    3.课程记录
        stark组件源码增加自动识别get_field_display()
    
    4.学习记录
        根据课程记录批量生成对应的学习记录
            利用actions自定义批量生成函数
        def patch_studyrecord(self,request,queryset):
            tmp = []
            for course_record in queryset:                                              student_list=Student.objects.filter(class_list__id=course_record.class_obj.pk)
            for student in student_list:
                obj = StudyRecord(student=student,course_record=course_record)
                tmp.append(obj)
            StudyRecord.objects.bulk_create(tmp)
    
    5.学习记录筛选
        筛选当天学习记录
        利用list_filter功能
            def get_filter(self,request,queryset):
            q = Q()
            for field,field_id in request.GET.items():
                if field in ['page','q']:
                    continue
                field_id = request.GET.get(field)
                q.children.append((field, field_id))
            queryset = queryset.filter(q)
            return queryset
        学习记录中自定义记录列点击记录跳转到对应的学生学习记录
    
        def record(self,is_header=False,obj=None):
            if is_header:
                return '记录'
            _url = '/stark/app01/studentstudyrecord/?classstudyrecord=%s'%obj.pk
            return mark_safe("<a href='%s'>记录</a>"%_url)
        list_display = ['class_obj','day_num',record]
        
    考勤功能
        def work(self,is_header=False,obj=None):
            if is_header:
                return '考勤'
            html = ''
            for k in obj.record_choices:
                if obj.record == k[0]:
                    s = "<option  value='%s' selected>%s</option>"%k
                else:
                    s = "<option  value='%s' >%s</option>"%k
                html += s
            return mark_safe("<select class='xxx' pk=%s>%s</select>"%(obj.pk,html))
    
        def check(self,request):
            status = request.POST.get('status')
            pk = request.POST.get('pk')
            print(status,pk)
            models.StudentStudyRecord.objects.filter(pk=pk).update(record=status)
            return HttpResponse('ok')
    
        def extra_url(self):
            tmp = []
            tmp.append(
                url(r'^check_work/',self.check)
            )
            return tmp
            
            
            window.onload = function () {
     $('.xxx').change(function () {
         var status = $(this).val();
         var pk = $(this).attr('pk');
         $.ajax({
             url:'/stark/app01/studentstudyrecord/check_work/',
             type:'post',
             data:{
                 status:status,
                 pk:pk
             },
             success:function (data) {
                 console.log(data)
             }
         })
     })
    };
    """
     
     
     
     

    Django知识点回顾

    """
    1.django请求生命周期
        http协议:
            四大特性:
                1.基于scoket应用于应用层的协议
                2.基于请求响应的协议
                3.无连接
                4.无状态
        WSGI协议:wsgiref,uwsgi,werkzeug实现模块
        中间件:默认七个中间件,五个可自定义方法,全局访问频率限制,权限限制等
            process_request  请求刚进来时
            process_view    经过url执行视图函数之前
            process_template_response   视图函数中return render时触发
            process_exception       视图函数中报错执行
            process_response    返回相应响应时
            
        urls.py:路由控制器
        MTV:
            views.py:
                视图层:FBV,CBV
                    CBV可张开讲:from django.views import View
                    看一下源码
                    
                返回结果必须是HttpResponse对象
                
            templates.py:
                模板语法:变量相关{{}},逻辑相关{%%}
                
            models.py:
                ORM操作
                
                
    2.django session机制
        request.session仅仅只是产生缓存,真正起作用还是中间件
        
    3.form组件 auth认证 简单提一嘴
    
    4.django settings文件配置
    
    5.前后端传输数据编码格式
        简单前后端传输页面代码搭建
        http传输数据格式书写
        三种常见编码格式总结
            urlencoded
            form-data
            json
        请求体数据格式推导(先瞎猜,再用事实验证)
        请求首行作用ContentType参数作用
        浏览器查看form表单默认提交方式以及组织数据格式
        ajax提交数据(默认提交,json提交>>>前后端分离)
            request.POST无法解析>>>request.body存储原始数据
            
    """

    admin使用

    """
    打开博客园django-admin组件,介绍admin是啥
        django自带的一个后台管理组件,快速的帮我们完成对数据的增删改查操作,我们这会是要学习admin的使用,并模仿它(及参考admin的源码)开发一套我们自己的后台管理工具
        
    新建项目,直接访问,需要超级用户
    博客园拷贝图书馆管理系统表格,数据库迁移并注册到admin中
    后台模型表展示
    
    添加一条数据后先总结url的规律
    模型表路由规律总结
        路由中都有admin是因为它是一个组件
        
        推导另外一个模型表的url规律
        总结:
        admin为每一个注册了的模型表创建增删改查四条url
    
    你们用admin应该只到这一步吧,接下来我们继续往深里看
    
    所有表的展示页面是不是都是一样的,现在我想对某个表设置不一样的展示,咋弄?
    再来五大重点参数介绍
        模型表继承admin.ModelAdmin
        
        list_display >>> 演示这个效果之后 看一点源码 配置类概念>>>阐释配置类概念
            为何要继承ModelAdmin
        
        list_display_links
        search_field
        list_filter
        actions
            讲action时,复习一个前端知识,单选多选getlist()
        
    1.启动源码
        django启动,执行每一个app下的admin.py文件
        原因:autodiscover_module('admin')
    
    单例模式复习
        对照博客
        重点在基于模块的单例模式上
            诠释模块导入重复导入只加载一次的特性pyc地方
            再写一个py文件,写一个函数里面调,然后去另外一个文件调函数
        再来个坑:导类过来,实例化两次,求打印结果?
    
    2.admin注册源码摘取精髓部分
    class AdminSite(object):
        def __init__(self, name='admin'):
            self._registry = {}
        def register(self, model_or_iterable, admin_class=None, **options):
            if not admin_class:
                admin_class = ModelAdmin
            self._registry[model] = admin_class(model, self)
    site=AdminSite()
    # **************************源码**************************
    admin.site.register(Book)
    admin.site.register(Book,BookConfig)
    引出配置类作用,self._registry中数据存放格式
    
    打印_registry里面数据带他们看看  这里慢点讲!!!
    """

    3.url设计

    """
    1.url分发本质
    url(r'^index/', ([],None,None))  # 固定写法元祖里面三个参数
    
    2.一级,二级分发演示
    url(r'^stark/',([],None,None))
    第一次推导
        url(r'^index/',([
                url(r'^test01/',test01),
                url(r'^test02/',test02)
        ],None,None))
    第二次推导
        url(r'^index/',([
            url(r'^test01/',([
                url(r'^test01_1',test01_1), 
                url(r'^test01_2',test01_2),
                url(r'^test01_3',test01_3),
            ],None,None)),
            url(r'^test02/',test02)
        ],None,None))
    
    
    自己生成注册了的模型表的url
    def list_view(request):
        return HttpResponse('list_view')
    def add_view(request):
        return HttpResponse('add_view')
    def edit_view(request,id):
        return HttpResponse('edit_view')
    def delete_view(request,id):
        return HttpResponse('delete_view')
    def get_urls1():
        tmp = [
            url(r'^$',list_view),
            url(r'^add/',add_view),
            url(r'^edit/(d+)/',edit_view),
            url(r'^delete/(d)/',delete_view)
        ]
        return tmp
    def get_urls():
        tmp = []
        for class_model,config_obj in admin.site._registry.items():
            app_label = class_model._meta.app_label
            model_name = class_model._meta.model_name
            tmp.append(
                url(r'%s/%s/'%(app_label,model_name),(get_urls1(),None,None))
            )
        return tmp
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^stark/',(get_urls(),None,None))
    ]
    
    """

    CRM项目开始

    """
    组件也是应用,一个项目有多个应用,一个应用可以用在多个项目里
    
    1.admin启动源码(django配置文件整体梳理>>>注册的app中admin)
    2.autodiscover_modules应用
    3.新建django项目>>>stark应用>>>service文件夹
    4.admin源码摘取 + 配置类
    5.url设计
        研究url(r'^admin/', admin.site.urls)源码
        一级分发
        二级分发(配置类介绍)
    6.总结回顾
        解释一下二级分发那个self到底是谁
        
    7.视图函数中self.model诠释
    8.展示各个模型表数据对象
    
    9.依据list_display详细展示数据对象
        表单展示
        表头展示
            verbose_name介绍 model._meta.get_field('字段名').verbose_name
        小bug调试,表头后端代码筛选__str__特殊情况
            对象反射拿__str__不报错的原因>>>联想到迭代器__next__,嫌麻烦,可以写成next()
            
    10.单张表的自定义数据列 选择 编辑 删除>>>拓展至全局配置
        详细讲
            先用户自定义 再转成全局配置
            获取表头表单内部代码优化
    
    11.选择 编辑 删除标识性参数设置
    
    12.反向解析
    
    13.list_display_links
            自定义编辑列根据list_display_links显隐展示
    """

    form与modelform

    """
    2.form组件介绍
        演示form本质应用
        class TestForm(forms.Form):
            name = forms.CharField(max_length=32)
            email = forms.EmailField()
            age = forms.IntegerField()
            
        python console中直接测试
        from app01 import views
        obj = views.TestForm({'name':'jason','email':'123','age':18})
        obj.is_valid()  # 校验数据
        False
        obj.cleaned_data  # 合格的放入这个里面
        {'name': 'jason', 'age': 18}
        obj.errors  # 不合格的放入errors中
        {'email': ['Enter a valid email address.']}
        
        继续演示几种传参类型
        obj = views.TestForm({'name':'jason','age':18})
        obj = views.TestForm({'name':'jason','email':"123@qq.com",'age':18})
        obj = views.TestForm({'name':'jason','email':"123@qq.com",'age':18,'abc':123})
        
    3.form组件完成
        class Book(models.Model):
            title = models.CharField(max_length=32)
            price = models.DecimalField(max_digits=8,decimal_places=2)
            date = models.DateField()
            publish = models.ForeignKey("Publish")
            authors = models.ManyToManyField("Author")
    
        class Publish(models.Model):
            name = models.CharField(max_length=32)
    
        class Author(models.Model):
            name = models.CharField(max_length=32)
    对上述三张表用form完成增  >>>  编辑功能form组件书写难度
    
    1.新建django项目 自定义模型表
    这里详细演戏图书管理系统的增删改查:
        1.先不借助任何组件,自己手动渲染标签
            这里需要注意的问题有前端input标签一定要写name属性
            后端在更新数据的时候,记得用关键字传参的形式去做
            新增用add(*author) 编辑用set(author)
            
        2.借助form组件
            前端三种渲染方式
            
        3.最后推到出modelform组件
            class BookModelForm(forms.ModelForm):
                class Meta:
                    model = models.Book
                    fields = '__all__'
    
    
    4.modelform介绍
    """
    """
    1.modelform完成CRM项目中模型表的增删改查
        这里为什么定义这个方法,主要是用户如果想针对自己的模型表进行其他的特定操作
        def get_model_form(self):
            if self.model_form_class:
                return self.model_form_class
            from django.forms import ModelForm
            class ModelFormClass(ModelForm):
                class Meta:
                    model = self.model
                    fields = '__all__'
            return ModelFormClass
            
            def add_view(self,request):
                model_form = self.get_model_form()
                if request.method == 'POST':
                    model_form_obj = model_form(request.POST)
                    if model_form_obj.is_valid():
                        model_form_obj.save()
                        return redirect(self.get_reverse_url('list'))
                model_form_obj = model_form()
                return render(request,'stark/add_view.html',locals())
                
            def edit_view(self,request,id):
                model_form = self.get_model_form()
                edit_obj = self.model.objects.filter(pk=id).first()
                if request.method == 'POST':
                    model_form_obj = model_form(request.POST,instance=edit_obj)
                    if model_form_obj.is_valid():
                        model_form_obj.save()
                        return redirect(self.get_reverse_url('list'))
                model_form_obj = model_form(instance=edit_obj)
                return render(request,'stark/edit_view.html',locals())
                
            def delete_view(self,request,id):
                delete_obj = self.model.objects.filter(pk=id).delete()
                return redirect(self.get_reverse_url('list'))
    """

    分页

    """
    1.批量插入数据方法
        传统:效率极低
            def index(request):
                for i in range(100):
                    Book.objects.create(title='book_%s'%i,price=i*i)
                return render(request,'book_list.html')
        现在:
            book_list = []  # 里面方法Book对象
            for i in range(100):
                book = Book(title="book_%s"%i,price=i*i)
                book_list.append(book)
            Book.objects.bulk_create(book_list)
    2.展示批量产生的数据 
    3.分页介绍
        博客园分页代码拷贝,解释
        保存搜索条件介绍(拉勾网展示)
        request.GET.urlencoded()构造get请求携带数据格式
    """

    数据展示类

    """
    1.数据展示全部封装到新的Showlist展示类中
    """

    search功能

    """
    1.bootstrap中找寻前端样式
        css样式   >>>     表单
    2.后端模拟简单版本实现效果
        # search功能  简单推导1
        key_word = request.GET.get('q')
        if key_word:
            for field in self.search_fields:
                queryset = self.model.objects.filter(title__icontains=key_word)
    3.如何解决查询条件关键字 title__icontains拼接?
        Q对象简介
            # python console中演示推导过程1
            from app01 import models
            from django.db.models import Q
            models.Book.objects.filter(title='西游记')
            <QuerySet [<Book: 西游记>]>
            models.Book.objects.filter(Q(title='西游记')|Q(price=399))
            <QuerySet [<Book: 西游记>, <Book: 水浒传>]>
            
            # 推导过程2
            q = Q()
            q.children.append(('title','西游记'))
            models.Book.objects.filter(q)
            <QuerySet [<Book: 西游记>]>
            
            # 推导过程3多个条件
            q = Q()
            q.children.append(('title','西游记'))
            models.Book.objects.filter(q)
            <QuerySet [<Book: 西游记>]>
            q.children.append(('price','399'))
            models.Book.objects.filter(q)
            <QuerySet []>
            
            # 最终推导
            from django.db.models import Q
            q = Q()
            q.children.append(("字段名","可能存在的字段值"))  # 特点在于 直接写的是字符串
            q.connector = "or"  # q添加数据默认是和的关系,可通过该参数改变
            Models.objects.filter(q)  # 以q里面添加的条件筛选相应数据
    4.要想实现模糊查询需要
        q.children.append(("字段名"+"__icontains","可能存在的字段值")) 
    5.search功能实现
    6.search功能封装配置类方法
    7.search功能前端搜索保留搜索条件
        配置类对象添加key_word属性
    8.search功能前端界面显隐
        根据key_word属性控制
    """

    action功能

    """
    1.django admin后台action功能演示
    2.前端样式
        <select class='form-control' name='action'>
            <option>----------------</option>
        </select>
        <input type='submit' class='btn btn-info'>
    3.点击提交按钮需要将checkbox选中数据与select选择框中的数据提交到后端,所以需要保证二者在一个form表单中
        <form action="" class="form-inline" method="post">
            {% csrf_token %}
                <select class="form-control s" name="action">
                <option>---------------------------------------</option>
                    {% for func_dic in show_obj.get_actions %}
                        <option value="{{ func_dic.name }}">{{ func_dic.desc }}</option>
                    {% endfor %}
            </select>
            <input type="submit" class="btn btn-warning" style="">
            <table class="table table-bordered table-striped table
       </form>
    4.actions属性 获取信息展示前端
        tmp = []
        for func in self.config_obj.actions:  # [patch_init,]
            tmp.append({
                "name": func.__name__,
                "desc": func.desc
            })
        return tmp
    5.action逻辑处理
        def list_view(self):
            if request.method == 'POST':
                action = request.POST.get('action')
                pk_list = request.POST.getlist('select_action')
                queryset = self.model.objects.filter(pk__in=pk_list)
                action_func = getattr(self,action)
                action_func(request, queryset)
            ...
            
    """

    filter功能

    """
    1.admin list_filter功能演示,展示的其实是一个个的数据对象
    2.前端页面搭建
        <div class="col-md-3">
            <div class="alert-info text-center">FILTER</div>
        </div>
    3.后端
        分析:
            1.list_filter中只能放外键字段
            2.先循环字段,取出该字段对应的模型表,再取每个字段对应的多组数据
                model._meta.get_field('字段').rel.to >>> 模型表
            3.深拷贝request.GET
                获取get请求参数
                保存搜索条件
            4.all标签渲染
                由于传输数据格式为[{'外键字段':[a,b,c,d...],'外键字段':[e,f,g,h...]}]
                每个外键字段对应的列表中都需要一个all标签,而all标签的作用就是去除其所在字段在url中的键值,也就是说只需要pop掉当前循环的字段即可
        
            def get_list_filter(self):
                tmp_dict = {}
                for field in self.config_obj.list_filter:
                    tmp = []
                    relation_model = self.config_obj.model._meta.get_field(field).rel.to
                    queryset = relation_model.objects.all()
                    import copy
                    params = copy.deepcopy(self.request.GET)
                    click_id = self.request.GET.get(field)
    
                    # all标签
                    params2 = copy.deepcopy(self.request.GET)
                    if field in params2:
                        params2.pop(field)
                        all_link = "<a href='?%s'>All</a>" % params2.urlencode()
                    else:
                        all_link = "<a href=''>All</a>"
                    tmp.append(mark_safe(all_link))
    
                    for data in queryset:
                        params[field] = data.pk
                        _url = params.urlencode()
                        if click_id == str(data.pk):
                            s = "<a href='?%s' class='active'>%s</a>" % (_url, data)
                        else:
                            s = '<a href="?%s">%s</a>' % (_url, data)
                        tmp.append(mark_safe(s))
                    tmp_dict[field] = tmp
                return tmp_dic
    4.前端
        <div class="col-md-3">
            <div class="alert-info text-center">FILTER</div>
            {% for k,v in show_obj.get_list_filter.items %}
                <div class="panel panel-default">
                    <div class="panel-heading">By {{ k|upper }}</div>
                    <div class="panel-body">
                        {% for data in v %}
                            <p>{{ data }}</p>
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
        </div>
    5.数据过滤
        外键字段查询时
            model.objects.filter(publish__id=1)
            model.objects.filter(publish=obj)
            同样也支持
            model.objects.filter(publish=1)  >>> 自动转换成第一种
        所以我们再利用q对象往children里面添加数据时可以直接('publishs',1)
        
    """

    open样式演示

    """
    新建django项目演示open方法
        1.window.open 方法
            window.open('https://www.baidu.com','','height=400,width=800')
        2.填写自己的路由,打开添加页面
        3.填写数据,window.opener调用父类方法
        4.如何将创建出来的数据渲染到父页面>>>后端将数据对象传递到子页面,子页面传到父页面
        5.window.open路由中添加前端标签id,后端接收再传递到子页面,子页面再传到父页面
            后端充当中间商,只做数据传输
    """

    pop功能

    """
    1.admin查看效果
    2.加号渲染(必须是一对多或多对多字段,如何判断?)
        我们后台循环的字段是model字段还是form字段?是form字段,意味着没法通过ForeignKey,ManyToManyField判断,因为是form字段
        后端先打印每个字段的类型发现均是BoundField类型,无法判断
        需要拿字段对象再点field属性才能取到真经
        isinstance可以判断多继承关系
        前端如何根据条件渲染这个加号呢?后端给field字段对象添加is_pop=True属性
    3.前端加号样式通过相对移动调节样式
    
    
    open样式演示完之后再回来
    1.加号事件绑定
        路由问题解决,不同加号对应不同的添加页面>>>字段对象能加is_pop属性,能不能再加一个url属性,到时候直接点拿出来就行了(注意一定要放到form字段属性中)
        
        后端form字段对象如何获取对应的model字段的字段名
            不能用self.model 因为它是当前访问的表的添加页面,而加号对应的另外两个表的添加页面,所以这里需要我们手动反向解析url
            需要获取到加号字段对应的模型表
                form字段.name >>> form字段对应的model字段字段名
                self.model._meta.get_filed(form字段.name).rel.to >>> 表
                _url = reverse("%s_%s_add",xx,yy) 解决添加url
                
        select标签id查找
            谷歌浏览器查看 id_publish,id_authors
            form字段.name 拿到对应model字段的字段名
                _url = _url+"?pop_back_id=id_"+form字段.name
                这是我们手动拼接,还可以自动拼接
                _url = _url +"?pop_back_id="+form字段.auto_id  
        
        后端添加页面不能直接返回pop页面,不然的话我们连正常打开的pop页面也会打不开,所以需要根据url中是否有pop_back_id来判断是正常访问还是加号跳转
        
        pop页面调用父页面传pop_back_id,pk,text
        
        父页面DOM操作添加标签
            新添加的数据需要添加selected属性
        
    """

    Stark组件Bug调整

    """
    数据展示的时候,点分页会报错,因为会把page=x传到了filter中,还有就是search查询中的q=x也会报错,所以在filter功能中加if判断去掉
        if field in ['page','q']
            continue
    还有就是这样写了之后如果在url中手动条件额外不存在的键值还是会报错
                    try:
                        queryset = queryset.filter(q)
                    except BaseException:
                        pass
    """

    Stark组件总结

    """
    从头到尾捋一遍
    """

    权限介绍

    """
    1.什么是权限?
        生活中到处都是权限,比如我们办公室密码门,视频vip,等等...
        再回来我们写的web应用,一个公司里面老板和员工用的同一个web服务,两者能干的事也是各不相同
        再来看Django admin应用,访问模型表的增删改查这是不是也是一种权限
        总的来说就是
    
        who             what            how
    
        哪些人对哪些资源拥有哪些操作的权限,看admin的url
    
        http://127.0.0.1:8000/admin/auth/user/对auth下面的user表数据查看权限
    
        http://127.0.0.1:8000/admin/auth/user/1/change/对auth下面的user表数据的修改权限
    
        http://127.0.0.1:8000/admin/auth/user/1/delete/对auth下面的user表数据的删除权限
    
        http://127.0.0.1:8000/admin/auth/user/add/对auth下面的user表数据的增加权限
    
        权限其实就是一个个的url,用户可以访问的url放在一个大列表里,访问对应url的时候,去列表里查找,如果有就代表可以访问,拥有该权限,没有就是禁止访问,没有权限。
    
    2.定义几张表模拟一下
        class User:
            pass
        class Permission:
            pass
        class user2permission:
            pass
        1.创建俩用户一个拥有查,一个拥有查改
        2.一下子来了十个员工,都拥有增删改查四个权限,那我需要在第三张表里面反复录入
    """

    RBAC(Roel Based Access Control)

    """
    1.介绍
    日常生活中的应用软件怎么区分权限的?普通用户,超级管理员,经理,职员...
    是不是都是基于角色的权限管理,每一个角色对应有一定的权限,用户只需按角色划分,就能有对应的权限
    
    2.表关系
    class User:
        pass
    class Role:
        pass
    class Permission:
        pass
    class user2role:
        pass
    class role2permission:
        pass
    一次性有100个员工,你会发现方案1和方案2差距很大!!!   
    """

    项目搭建

    """
    1.新建django项目,stark组件移植过来
        知识点:
            templates文件查找会先从项目根目录找,其次会去应用下面找,再找不到报错
            static文件亦是如此,注意在拖拽的时候,防止pycharm自动加stark前缀,需要手动去除掉,任然以staic开头
            
    2.创建模型表并注册到stark组件中
        class User(models.Model):
            name = ...
            pwd = ...
            roles = ...
        class Role(model.Model):
            title = ...
            permissions = ...
        class Permission(model.Model):
            title = ...
            url = ...
    3.启动项目 添加数据
        权限表 
            角色和用户的增删改查
        角色表
            销售      查看 添加用户
            讲师      查看
            CEO      用户的增删改查
            管理员     所有权限
        用户表
            kevin   销售+讲师
            egon    CEO
            jason   管理员
    
    权限思路:
        1.创建表,录入数据
        2.登陆验证,获取登陆人的权限
        3.权限校验
    
    5.登陆校验获取登陆人的权限
        简单的前端登陆页面
        后端获取查询校验
            需要慢一点的地方在于如何基于用户对象查询权限信息
                user_obj.roles.all()  >>> 角色对象
                user_obj.roles.all().values('permission__url')
        权限列表构造
        session保存
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            user_obj = models.User.objects.filter(name=username,pwd=password).first()
            if user_obj:
                # 记录登陆状态
                request.session['user'] = username
                # 记录权限列表
                permission_list = []
                res = user_obj.roles.values('permissions__url').distinct()
                for permission in res:
                    permission_list.append(permission.get('permissions__url'))
                request.session['permission_list'] = permission_list
        return render(request,'login.html')
    """

    中间件校验权限

    """
    整体流程:
        1.用户访问白名单
            利用正则
        2.判断用户是否登陆
        3.获取用户权限并进行校验
            利用正则(并需要手动加限制"^$")
            
    注册到settings文件中
    
    讲课推理:
        1.判断用户是否登陆
        2.获取权限校验
            for循环直接判断在不在
            正则校验判断
            # 判断是否有权访问
            import re
            for permission_rex in permission_list:
                permission_rex = '^%s$'%permission_rex
                res = re.search(permission_rex,current_path)
                if res:
                    return None
            return HttpResponse('没有访问权限')
            
        3.用户访问白名单
            放开以admin打头的所有访问权限(正则)
            
            
    """

    前端样式调整

    """
    1.模板中加导航条和侧边栏以及整体样式微调
    """

    权限展示

    """
    1.前端页面需要展示菜单权限,但是并不是所有权限都要展示,也并不是都展示在一个主菜单下(通常只展示查看权限),所以我们应该想个办法区分权限的种类>>>权限表添加code字段标识权限种类
    (首页导航条右侧登陆用户展示,注销按钮)
    2.后端login视图函数中除了之前构建的权限列表外,需要再构建当前登录用户的权限名称,url,权限类型的列表套字典格式方便左侧菜单渲染
                # 菜单权限展示
                permission_menu_list = []
                for permission in permissions:
                    permissions_list.append(permission.get('permissions__url'))
                    if permission.get('permissions__code') == 'list':
                        permission_menu_list.append({
                            'title':permission.get("permissions__title"),
                            'url':permission.get("permissions__url")
                        })
                # 存储菜单权限
                request.session['permission_menu_list'] = permission_menu_list
    """

    CRM关系表

    from django.db import models
    
    # Create your models here.
    class User(models.Model):
        name = models.CharField(max_length=64)
        password = models.CharField(max_length=128)
        roles = models.ManyToManyField(to='Role')
    
        def __str__(self):
            return self.name
    
    class Role(models.Model):
        title = models.CharField(max_length=64)
        permissions = models.ManyToManyField(to='Permission')
    
        def __str__(self):
            return self.title
    
    class Permission(models.Model):
        title = models.CharField(max_length=64)
        url = models.CharField(max_length=128)
        code = models.CharField(max_length=64,default='list')
    
        def __str__(self):
            return self.title
    
    
    # CRM关系表
    class Department(models.Model):
        """
        部门表
        市场部     1000
        销售       1001
    
        """
        title = models.CharField(verbose_name='部门名称', max_length=16)
        code = models.IntegerField(verbose_name='部门编号', unique=True, null=False)
    
        def __str__(self):
            return self.title
    
    
    class UserInfo(models.Model):
        """
        员工表
        """
    
        name = models.CharField(verbose_name='员工姓名', max_length=16)
        email = models.EmailField(verbose_name='邮箱', max_length=64)
        depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code")
        user=models.OneToOneField("User",default=1)
        def __str__(self):
            return self.name
    
    
    class Course(models.Model):
        """
        课程表
        如:
            Linux基础
            Linux架构师
            Python自动化开发精英班
            Python自动化开发架构师班
            Python基础班
            go基础班
        """
        name = models.CharField(verbose_name='课程名称', max_length=32)
    
        def __str__(self):
            return self.name
    
    
    class School(models.Model):
        """
        校区表
        如:
            北京沙河校区
            上海校区
    
        """
        title = models.CharField(verbose_name='校区名称', max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class ClassList(models.Model):
        """
        班级表
        如:
            Python全栈  面授班  5期  10000  2017-11-11  2018-5-11
        """
        school = models.ForeignKey(verbose_name='校区', to='School')
        course = models.ForeignKey(verbose_name='课程名称', to='Course')
        semester = models.IntegerField(verbose_name="班级(期)")
    
    
        price = models.IntegerField(verbose_name="学费")
        start_date = models.DateField(verbose_name="开班日期")
        graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
        memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )
    
        teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo')
        tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo',related_name="class_list")
    
    
        def __str__(self):
            return "{0}({1}期)".format(self.course.name, self.semester)
    
    
    class Customer(models.Model):
        """
        客户表
        """
        qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一')
    
        name = models.CharField(verbose_name='学生姓名', max_length=16)
        gender_choices = ((1, '男'), (2, '女'))
        gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
    
        education_choices = (
            (1, '重点大学'),
            (2, '普通本科'),
            (3, '独立院校'),
            (4, '民办本科'),
            (5, '大专'),
            (6, '民办专科'),
            (7, '高中'),
            (8, '其他')
        )
        education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
        graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
        major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True)
    
        experience_choices = [
            (1, '在校生'),
            (2, '应届毕业'),
            (3, '半年以内'),
            (4, '半年至一年'),
            (5, '一年至三年'),
            (6, '三年至五年'),
            (7, '五年以上'),
        ]
        experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
        work_status_choices = [
            (1, '在职'),
            (2, '无业')
        ]
        work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
                                          null=True)
        company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
        salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True)
    
        source_choices = [
            (1, "qq群"),
            (2, "内部转介绍"),
            (3, "官方网站"),
            (4, "百度推广"),
            (5, "360推广"),
            (6, "搜狗推广"),
            (7, "腾讯课堂"),
            (8, "广点通"),
            (9, "高校宣讲"),
            (10, "渠道代理"),
            (11, "51cto"),
            (12, "智汇推"),
            (13, "网盟"),
            (14, "DSP"),
            (15, "SEO"),
            (16, "其它"),
        ]
        source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
        referral_from = models.ForeignKey(
            'self',
            blank=True,
            null=True,
            verbose_name="转介绍自学员",
            help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
            related_name="internal_referral"
        )
        course = models.ManyToManyField(verbose_name="咨询课程", to="Course")
    
        status_choices = [
            (1, "已报名"),
            (2, "未报名")
        ]
        status = models.IntegerField(
            verbose_name="状态",
            choices=status_choices,
            default=2,
            help_text=u"选择客户此时的状态"
        )
    
        consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter' )
    
        date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
        recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True)
        last_consult_date = models.DateField(verbose_name="最后跟进日期", )
    
        def __str__(self):
            return self.name
    
    
    class ConsultRecord(models.Model):
        """
        客户跟进记录
        """
        customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer')
        consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo')
        date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
        note = models.TextField(verbose_name="跟进内容...")
    
        def __str__(self):
            return self.customer.name + ":" + self.consultant.name
    
    
    class Student(models.Model):
        """
        学生表(已报名)
        """
        customer = models.OneToOneField(verbose_name='客户信息', to='Customer')
        class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True)
    
        emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
        company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
        location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
        position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
        salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
        welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
        date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
        memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)
    
        def __str__(self):
            return self.customer.name
    
    
    class ClassStudyRecord(models.Model):
        """
        上课记录表 (班级记录)
        """
        class_obj = models.ForeignKey(verbose_name="班级", to="ClassList")
        day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")
        teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo')
        date = models.DateField(verbose_name="上课日期", auto_now_add=True)
    
        course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
        course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
        has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
        homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
        homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
        exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)
    
        def __str__(self):
            return "{0} day{1}".format(self.class_obj, self.day_num)
    
    
    class StudentStudyRecord(models.Model):
        '''
        学生学习记录
        '''
        classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord")
        student = models.ForeignKey(verbose_name="学员", to='Student')
    
        record_choices = (('checked', "已签到"),
                          ('vacate', "请假"),
                          ('late', "迟到"),
                          ('noshow', "缺勤"),
                          ('leave_early', "早退"),
                          )
        record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
        score_choices = ((100, 'A+'),
                         (90, 'A'),
                         (85, 'B+'),
                         (80, 'B'),
                         (70, 'B-'),
                         (60, 'C+'),
                         (50, 'C'),
                         (40, 'C-'),
                         (0, ' D'),
                         (-1, 'N/A'),
                         (-100, 'COPY'),
                         (-1000, 'FAIL'),
                         )
        score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
        homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
        note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)
    
        homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
        stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
        date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)
    
        def __str__(self):
            return "{0}-{1}".format(self.classstudyrecord, self.student)
    

    数据录入

    """
    学校
    课程
    部门
    用户信息
    班级
    
    
    按照models.py中模型表的顺序依次添加,添加过程中需要引入的知识点有
    1.添加班级表记录时,授课老师和班主任信息展示需要用到limit_choices_to
        limit_choices_to={'depart_id':170}  >>>     单个部门
        limit_choices_to={'depart_id__in':[120,150]}    >>>     多个部门
    
    2.客户表
        录入数据后,展示客户性别引出get_field_display()展示choice字段对应的注解
            def display_gender(self,is_header=False,obj=None):
                if is_header:
                    return '性别'
                return obj.get_gender_display()
            按照上述方式,只要是有choice字段的都可按照此法展示
    
    3.展示咨询课程
        3.1将咨询课程全部做成a标签的样式,然后提出a标签的需求:
            点哪个a标签就将该a标签对应的客户咨询课程删除
        3.2删除的是某一客户的咨询课程,而我们stark组件只会为每一个模型表创建增删改查四条基本的url,这个问题如何解决?
            先去stark组件源码二级路由分发处演示思路,直接在get_urls()演示
                def get_urls(self):
                    tmp = [
                        url(r'^$',self.list_view,name='%s_%s_list'%(self.app_label,self.model_name)),
                        url(r'^add/',self.add_view,name='%s_%s_add'%(self.app_label,self.model_name)),
                        url(r'^edit/(d+)/',self.edit_view,name='%s_%s_edit'%(self.app_label,self.model_name)),
                        url(r'^delete/(d+)/',self.delete_view,name='%s_%s_delete'%(self.app_label,self.model_name))
                    ]
                    tmp.append(
                        url(r'^.../',删除客户咨询某一课程记录)
                    )
                    return tmp
           这样做的缺陷就是会生成很多条相同的url,如何做到只为某一个模型表配置额外的url?
           (你们想想list_display,actions这些功能是怎么玩的???)
           a标签渲染
            def display_course(self,is_header=False,obj=None):
                if is_header:
                    return '咨询课程'
                course_list =  []
                for course in obj.course.all():
                    course_list.append("<a href='/stark/app01/customer/cancel_course/%s/%s/' style='border:1px solid #369;padding:3px 6px'>%s</a>"%(obj.pk,course.pk,course.name))
                return mark_safe(' '.join(course_list))
                
           自定义扩张url方法
            def extra_url(self):
                tmp = []
                tmp.append(
                url(r'^cancel_course/(d+)/(d+)/',self.cancel_course)
                )
                return tmp
                
            自定义视图函数
            def cancel_course(self,request,customer_id,course_id):
                customer_obj = models.Customer.objects.filter(pk=customer_id).first()
                customer_obj.course.remove(course_id)
                return redirect(self.get_reverse_url('list'))
                
            stark组件新增一个空的extra_url方法,用户不配置就无任何多余的url
            def extra_url(self):
                return []   
    """

    剩余四张表

    """
    1.咨询记录表信息录入
    
    2.学生信息录入
    
    3.课程记录
        stark组件源码增加自动识别get_field_display()
    
    4.学习记录
        根据课程记录批量生成对应的学习记录
            利用actions自定义批量生成函数
        def patch_studyrecord(self,request,queryset):
            tmp = []
            for course_record in queryset:                                              student_list=Student.objects.filter(class_list__id=course_record.class_obj.pk)
            for student in student_list:
                obj = StudyRecord(student=student,course_record=course_record)
                tmp.append(obj)
            StudyRecord.objects.bulk_create(tmp)
    
    5.学习记录筛选
        筛选当天学习记录
        利用list_filter功能
            def get_filter(self,request,queryset):
            q = Q()
            for field,field_id in request.GET.items():
                if field in ['page','q']:
                    continue
                field_id = request.GET.get(field)
                q.children.append((field, field_id))
            queryset = queryset.filter(q)
            return queryset
        学习记录中自定义记录列点击记录跳转到对应的学生学习记录
    
        def record(self,is_header=False,obj=None):
            if is_header:
                return '记录'
            _url = '/stark/app01/studentstudyrecord/?classstudyrecord=%s'%obj.pk
            return mark_safe("<a href='%s'>记录</a>"%_url)
        list_display = ['class_obj','day_num',record]
        
    考勤功能
        def work(self,is_header=False,obj=None):
            if is_header:
                return '考勤'
            html = ''
            for k in obj.record_choices:
                if obj.record == k[0]:
                    s = "<option  value='%s' selected>%s</option>"%k
                else:
                    s = "<option  value='%s' >%s</option>"%k
                html += s
            return mark_safe("<select class='xxx' pk=%s>%s</select>"%(obj.pk,html))
    
        def check(self,request):
            status = request.POST.get('status')
            pk = request.POST.get('pk')
            print(status,pk)
            models.StudentStudyRecord.objects.filter(pk=pk).update(record=status)
            return HttpResponse('ok')
    
        def extra_url(self):
            tmp = []
            tmp.append(
                url(r'^check_work/',self.check)
            )
            return tmp
            
            
            window.onload = function () {
     $('.xxx').change(function () {
         var status = $(this).val();
         var pk = $(this).attr('pk');
         $.ajax({
             url:'/stark/app01/studentstudyrecord/check_work/',
             type:'post',
             data:{
                 status:status,
                 pk:pk
             },
             success:function (data) {
                 console.log(data)
             }
         })
     })
    };
    """
  • 相关阅读:
    测试平台系列(69) 数据构造器支持sql语句
    Selenium获取动态图片验证码
    测试平台系列(68) 解决数据驱动带来的麻烦
    测试平台系列(67) 玩转数据驱动
    3班6组项目测试心得
    3班6组第一次迭代博客
    需求分析心得
    数据库设计心得
    结对编程总结
    代码欣赏
  • 原文地址:https://www.cnblogs.com/pdun/p/10839408.html
Copyright © 2020-2023  润新知