• python---CRM用户关系管理


     

    Day1:项目分析

    一:需求分析

    二:CRM角色功能介绍

    三:业务场景分析

    销售:
        1.销售A 从百度推广获取了一个客户,录入了CRM系统,咨询了Python课程,但是没有报名
        2.销售B 从qq群获取一个客户,成功使他报名Python班,然后给他发送了报名连接,等待用户填写完毕后,将他添加到Python具体的学习班级中
        3.销售C 打电话给之前的一个客户,说服他报名Python课程,但是没有成功,更新了跟踪记录
        4.销售D 获取了一个客户,录入信息时,发现此客户已经存在,不允许重复录入,随后通知相应的原负责人跟进
        5.销售E 从客户库中获取了,超过一个月未跟进的客户,进行再次跟进
        6.销售主管 查看了部门本月的销售报表,包括来源分析,成单率分析,班级报名数量分析,销售额环比,同比
    学员:
        1.客户A 填写了销售发来的报名连接,上传了个人的证件信息,提交,之后收到邮件,告知报名成功,并为他开通了学员账号,升级为学员A
        2.学员A 登录学员系统,看到自己的合同,报名的班级,课程大纲
        3.学员A 提交了Python课程当时课时作业
        4.学员A 查看自己的Python课程成绩,排名
        5.学员A 搜索问题,未找到答案,录入一条问题
        6.学员A 转介绍学员,录入其信息
    讲师:
        1.讲师A 登录CRM系统,查看自己管理的班级列表
        2.讲师A 进入Python 5期课程,创建第3节的上课记录,填写了本节课内容,作业要求
        3.讲师A 在课程中点名,对点名情况进行录入,标记相关状态
        4.讲师A 批量下载所有学员的课时作业,给每个学员在线批注了成绩+状态
        
    管理员:
        1.创建课程 C++,Python..
        2.创建校区 上海,北京..
        3.创建班级 C++35期,Python27期
        4.创建账号 ABCD
        5.创建了销售,讲师,学员角色
        6.为账号分配到对应的角色,将ABCD分配给销售
        7.创建相关权限
        8.为销售角色分配了相关权限

    四:表结构设计

    数据库关联模型

    Django表结构实现

    from django.db import models
    from django.contrib.auth.models import User
    # Create your models here.
    
    class UserProfile(models.Model):
        '''
        用户信息表:
        含有讲师,销售,管理员这些正式人员
        '''
        user = models.OneToOneField(User)  #使用的是Django自带的用户验证Username, password and email are required. Other fields are optional.
    
        name = models.CharField(max_length=64,verbose_name="姓名")
        role = models.ManyToManyField("Role",blank=True)  #,null=Truenull has no effect on ManyToManyField.,null对于manytomanyfield无作用,会报警
    
        def __str__(self):
            return self.name
    
    class Role(models.Model):
        '''
        角色表:学员,讲师,销售,管理员
        '''
        name = models.CharField(max_length=64,unique=True)
        menus = models.ManyToManyField("Menu",blank=True)
        def __str__(self):
            return self.name
    
    class Menu(models.Model):
        '''
        动态菜单
        '''
        name = models.CharField("菜单名",max_length=64)
        url_type_choices = (
            (0,"absolute"), #绝对路径/Sale/index.html
            (1,"dynamic"),  #动态url,根据url()方法中的name获取
        )
    
        url_type = models.SmallIntegerField(choices=url_type_choices)
        url_name = models.CharField("URL",max_length=128)
    
        def __str__(self):
            return self.name
    
    class CustumerInfo(models.Model):
        '''
        客户信息表:联系方式,姓名等
        '''
    
        name = models.CharField(max_length=64,null=True,blank=True) #开始咨询的时候允许为空
        contact_type_choices = ((0,'qq'),(1,"微信"),(2,'手机'))
        contact_type = models.SmallIntegerField(choices=contact_type_choices,default=0)
        contact = models.CharField(max_length=64,unique=True)
        source_choices = (
            (0,'QQ群'),
            (1,"51CTO"),
            (2,"百度推广"),
            (3,"知乎"),
            (4,"转介绍"),
            (5,"其他")
        )
        source = models.SmallIntegerField(choices=source_choices)
        referral_from = models.ForeignKey("self",blank=True,null=True,verbose_name="转介绍人员")
    
        consult_courses = models.ManyToManyField("Course",verbose_name="咨询课程")  #咨询的课程,允许咨询多门
        consult_content = models.TextField("咨询内容",blank=True)
        status_choices = ((0,"未报名"),(1,"已报名"),(2,"已退学"))
        status = models.SmallIntegerField(choices=status_choices)
    
        consultant = models.ForeignKey("UserProfile",verbose_name="课程顾问")
    
        date = models.DateField(auto_now_add=True)
    
        def __str__(self):
            return self.name
    
    class CustumerFollowUp(models.Model):
        '''
        客户跟踪记录表:跟踪进度
        '''
        customer = models.ForeignKey("CustumerInfo")
        content = models.TextField(verbose_name="跟进内容")
        user = models.ForeignKey("UserProfile",verbose_name="跟进人员")
        status_choices = (
            (0,"近期无报名计划"),
            (1,"一个月内报名"),
            (2,"2周内报名"),
            (3,"已报名"),
        )
    
        status = models.SmallIntegerField(choices=status_choices)
        date = models.DateField(auto_now_add=True)
    
        def __str__(self):
            return self.content
    
    class Student(models.Model):
        '''
        学员信息表:(未报名的客户在客户表中),报名成功的在学员表
        '''
        customer = models.ForeignKey("CustumerInfo")
        class_grades = models.ManyToManyField("ClassList")  #学员可以报多门课程
    
        def __str__(self):
            return self.customer.name
    
    class Course(models.Model):
        '''
        课程表
        '''
        name = models.CharField(max_length=64,verbose_name="课程名称",unique=True)
        price = models.PositiveSmallIntegerField()  #必须为正
        period = models.PositiveSmallIntegerField(verbose_name="课程周期(月)",default=5)
        outline = models.TextField(verbose_name="大纲")
    
        def __str__(self):
            return self.name
    
    class ClassList(models.Model):
        '''
        班级列表
        '''
        branch = models.ForeignKey("Branch")    #校区关联
        couser = models.ForeignKey("Course")
    
        class_type_choices = (
            (0,"脱产"),
            (1,"周末"),
            (2,"网络班")
        )
        class_type = models.SmallIntegerField(choices=class_type_choices,default=0)
    
        semester = models.SmallIntegerField(verbose_name="学期")
        teachers = models.ManyToManyField("UserProfile",verbose_name="讲师")
        start_date = models.DateField("开班日期")
        graduate_date = models.DateField("毕业日期",blank=True,null=True)
    
        def __str__(self):
            return "%s (%s)期"%(self.couser,self.semester)
    
        class Meta:
            unique_together = ('branch','class_type',"couser","semester") #联合唯一
    
    class CourseRecord(models.Model):
        '''
        上课记录:该节课程内容等
        '''
        class_grade = models.ForeignKey("ClassList",verbose_name="上课班级")
        day_num = models.PositiveSmallIntegerField(verbose_name="课程节次")
        teacher = models.ForeignKey("UserProfile")
        title = models.CharField("本节主题",max_length=64)
        content = models.TextField("本节内容")
        has_homework = models.BooleanField("本节是否有作业",default=True)
        homework = models.TextField("作业需求",blank=True,null=True)
        date = models.DateTimeField(auto_now_add=True,verbose_name="上课时间")
        def __str__(self):
            return "%s第(%s)节"%(self.class_grade,self.day_num )
    
        class Meta:
            unique_together = ("class_grade","day_num")
    
    class StudyRecord(models.Model):
        '''
        学习记录表:学员考勤,作业,成绩,备注
        '''
        course_record = models.ForeignKey("CourseRecord")
        student = models.ForeignKey("Student")
    
        score_choices = (
            (100,"A+"),
            (90,"A"),
            (85,"B+"),
            (80,"B"),
            (75,"B-"),
            (70,"C+"),
            (60,"C"),
            (40,"C-"),
            (0,"N/A"),  #不可得not avaliable
            (-50, "D"), #未交作业
            (-100,"COPY")   #抄袭
        )
        score = models.SmallIntegerField(choices=score_choices)
    
        show_choices = (
            (0,"缺勤"),
            (1,"已签到"),
            (2,"迟到"),
            (3,"早退"),
        )
        show_status = models.SmallIntegerField(choices=show_choices)
    
        note = models.CharField("情况备注",max_length=128,blank=True,null=True)
    
        date = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return "%s %s %s"%(self.course_record,self.student,self.score)
    
    class Branch(models.Model):
        '''
        校区
        '''
        name = models.CharField(max_length=64,unique=True)
        addr = models.CharField(max_length=128,blank=True,null=True)
        def __str__(self):
            return self.name
    表结构创建

     Day2:主要实现功能kingadmin为各个应用实现一个类似于Django自带的数据库管理功能

      kingadmin目录        

        销售目录

        学员目录

    1.首先我们需要在项目启动后(进入Kingadmin模块中view视图后,能够自动采集所有的应用中需要我们采集的数据库信息)

     (1)先设置采集方法:在每个需要我们采集的应用模块中添加上kingadmin.py文件(类似于后台admin会在应用模块的admin.py中采集信息一样)。如上面目录结构,在其中添加了kingadmin.py

    from kingadmin.sites import site    #虽然说,每个APP:sale,student都去导入了一次site,但是在python项目中对于同一个模块只会导入一次,所以这本身就是单例模式(使用的是内存中存在的那个)
    from kingadmin.admin_base import BaseKingAdmin
    from repository import models
    
    print("Sale.kingadmin")
    
    class CustomerAdmin(BaseKingAdmin):
        list_display = ['name','contact_type','contact','source','consult_content','consultant','status','date']
        list_filter = ['source','consultant','status','date']
        search_fields = ['contact','consultant__name']
    
    site.register(models.CustumerInfo,CustomerAdmin)
    site.register(models.Role)
    site.register(models.Menu)
    site.register(models.UserProfile)
    Sale模块中kingadmin
    from kingadmin.sites import site
    from kingadmin.admin_base import BaseKingAdmin
    from Student import models
    
    class TestAdmin(BaseKingAdmin):
        list_display = ['name']
    
    site.register(models.TestAdmin,TestAdmin)
    
    
    
    ----------------------------------------------------------
    student模块中自定义一个表
    class TestAdmin(models.Model):
        name = models.CharField("姓名",max_length=64)
    
        def __str__(self):
            return self.name
    Student模块中kingadmin

    从中发现需要用到一个基类BaseKingAdmin来自于kingadmin模块:是为了防止注册事件时出现为空的现象,而且在基类中添加功能更加方便

    class BaseKingAdmin(object):
        pass
    admin_base.py中BaseKingAdmin基类

    还需要from kingadmin.sites import site,使用到site方法(类似于admin.site.register(模型,自定义模型显示类)):功能是将各个模块中的数据模型统一添加在一个数据结构中,方便调用

    from kingadmin.admin_base import BaseKingAdmin
    
    class AdminSite(object):
        def __init__(self):
            self.enabled_admins = {}
    
        def register(self,model_class,admin_class=None):
            '''
            注册admin表
            :param model_class:
            :param admin_class:
            :return:
            '''
            app_name = model_class._meta.app_label  #app_label是当前应用的名字  一个应用可以注册多个表
            model_name = model_class._meta.model_name #model_name是表名    和app_lable连接就是数据表全名
    
            if not admin_class:
                admin_class = BaseKingAdmin()
            else:
                admin_class = admin_class()
    
            if app_name not in self.enabled_admins:
                self.enabled_admins[app_name] = {}
    
            admin_class.model = model_class
    
            self.enabled_admins[app_name][model_name] = admin_class
    
    site = AdminSite()
    sites.py中的site方法

    将数据统一放入self.enabled_admins{}中,形式为self.enabled_admins[模块][表名] = 自定义模型显示类(默认BaseKingAdmin)

    注意:虽然在每个模块中都导入了一次sites模块,使用一次site对象,实际上使用的是同一个site对象

    可以使用id(site)查看内存,因为python机制中将一个模块导入后,会将其保存在内存中,下次导入数据的时候,会直接从内存中获取数据(所以大家使用的是一个site对象)
    所以说:python模块本身就是单例模式

     (2)从settings.py中获取各个模块。创建app_setup.py文件,在项目进入view时去调用该文件,并执行,获取到所有模块的信息

    进入views.py自动调用app_setup.kingadmin_auto_discover()方法

    from django.shortcuts import render,redirect
    from django.contrib.auth import authenticate,login,logout   #快捷操作
    from kingadmin import app_setup
    
    app_setup.kingadmin_auto_discover() #用来导入所有含Kingadmin的模块,模块中会去调用相应的Kingadmin文件去注册事件
    
    from kingadmin.sites import site  #发现只导入模块一次,site对象只有一个
    
    ---------------------下面实现的是将数据分发给前端-------------------------------------
    def get_filter_result(request,querysets):
        filter_conditions = {}
        for k,v in request.GET.items():
            if v:
                filter_conditions[k] = v
        return querysets.filter(**filter_conditions),filter_conditions
    
    def table_obj_list(request,app_name,model_name):
        '''取出指定的数据返给前端'''
    
        admin_class = site.enabled_admins[app_name][model_name]
    
        model_class = admin_class.model
        querysets = model_class.objects.all()
    
        filter_data,filter_conditions = get_filter_result(request,querysets)
        print(filter_conditions)
        admin_class.filter_conditions = filter_conditions   #也可以传值给前端,但是这样也不错
    
    
        return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class})
    views.py进入后,顺序执行,首先去调用app_setup.kingadmin_auto_discover()方法采集信息

    看如何采集各个模块信息:从配置文件中settings的INSTALLED_APPS中获取所有模块信息

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'repository.apps.RepositoryConfig',
        'kingadmin',
        'Student',
        'Sale',
    ]
    settings文件INSTALLED_APPS

    views调用了app_setup中的kingadmin_auto_discover()方法自动采集信息,下面看看app_setup文件:实现方法。反向查找

    from django import conf  #实现动态获取配置文件,而不是以目录形式
    import importlib
    
    def kingadmin_auto_discover():
        for module in conf.settings.INSTALLED_APPS:
            try:
                # md = importlib.import_module('.kingadmin',module)  #这个也可以
                md = __import__('%s.kingadmin'%module)  #导入Kingadmin,然后回去执行该文件中的数据,去注册事件(模块导入后,会自动使用site.register方法注册事件)
            except ImportError as e:
                pass

    (3)上面将数据采集完毕,方法内存中site对象中,使用app_index视图方法,可以实现后台管理admin首页功能

    def app_index(request):return render(request,"kingadmin/app_index.html",{'site':site})
        <div>
        {% for app_name,app_tables in site.enabled_admins.items %}
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>{{ app_name }}</th>
                    </tr>
                </thead>
                <tbody>
                    {%  for model_name in app_tables %}
                        <tr>
                            <td>
                                <a href="{% url 'table_obj_list' app_name model_name %}">
                                    {{ model_name }}
                                </a>
                            </td>
                            <td>ADD</td>
                            <td>Change</td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        {% endfor %}
        </div>
    前端主要代码

    (4)实现点击表名,查看数据的功能

    def get_filter_result(request,querysets):
        filter_conditions = {}
        for k,v in request.GET.items():
            if v:
                filter_conditions[k] = v
        return querysets.filter(**filter_conditions),filter_conditions
    
    def table_obj_list(request,app_name,model_name):
        '''取出指定的数据返给前端'''
    
        admin_class = site.enabled_admins[app_name][model_name]
    
        model_class = admin_class.model
        querysets = model_class.objects.all()
    
        filter_data,filter_conditions = get_filter_result(request,querysets)
        print(filter_conditions)
        admin_class.filter_conditions = filter_conditions   #也可以传值给前端,但是这样也不错
    
    
        return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class})
    table_obj_list方法根据模块和表名去获取site对象中的数据
    {% extends "kingadmin/index.html" %}
    {% load my_func %}
    
    {% block right-content-container %}
        <h1 class="page-header">APP</h1>
        <form method="get">
        {% for field in admin_class.list_filter %}
            {% build_filter_row field admin_class %}
        {% endfor %}
        <button type="submit" class="btn btn-primary">提交</button>
        </form>
        <div>
            <table class="table table-striped">
                <thead>
                    <tr>
                        {% for field in admin_class.list_display %}
                            <th>{{ field }}</th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody>
                    {% for item in queryset %}
                        <tr>
                        {% build_table_row item admin_class %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    
    
    {% endblock %}
    前端

    前端使用了自定义模板函数

    # coding:utf8
    # __author:  Administrator
    # date:      2018/5/26 0026
    # /usr/bin/env python
    from django import template
    from django.utils.safestring import mark_safe
    from datetime import datetime,timedelta
    
    register = template.Library()
    
    @register.simple_tag
    def build_filter_row(field,admin_class):
        model = admin_class.model
        field_obj = model._meta.get_field(field)
        filter_conditions = admin_class.filter_conditions
        try:
            select = "<select name='%s'>"%field
            data_list = field_obj.get_choices(field) #可以获取choices选项和外键
        except AttributeError:
            if field_obj.get_internal_type() in ("DateField","DateTimeField"):
                field = "%s__gte"%field
                select = "<select name='%s'>"%field
                time_now = datetime.now()
                time_list = [
                    ["", "---------"],
                    [time_now, "today"],
                    [time_now - timedelta(7), "七天内"],
                    [time_now.replace(day=1), "本月"],
                    [time_now - timedelta(90), "三个月内"],
                    [time_now.replace(month=1, day=1), "今年内"],
                    ['', "ALL"]
                ]
    
                def turn_date(date_list):
                    date_obj, date_str = date_list
                    if type(date_obj) is datetime:
                        date_obj = date_obj.strftime("%Y-%m-%d")
                    return (date_obj,date_str)
    
                data_list = map(turn_date,time_list)
            else:
                select = "<select name='%s'>"%field
                data_list = list(model.objects.values_list("id",field))
    
        for item in data_list:
            if str(item[0]) == filter_conditions.get(field,None):
                option = "<option value='%s' selected>" % str(item[0])
            else:
                option = "<option value='%s'>"%str(item[0])
            option += item[1]
            option += "</option>"
            select += option
    
        select += "</select>"
    
        return mark_safe(select)
    
    @register.simple_tag
    def build_table_row(obj,admin_class):
        '''生成一条HTML中tr元素'''
        tr = ""
        for field in admin_class.list_display:
            # column_obj = admin_class.model._meta.get_field(field)    #model是获取对应的模型对象
            # if column_obj.choices:
            #     column_data = getattr(obj,"get_%s_display"%field)() #使用方法,要加上()
            # else:
            #     column_data = getattr(obj,field) #使用属性不需要()
            func = "get_"+field+"_display"
            if hasattr(obj,func):
                column_data = getattr(obj,func)()
            else:
                column_data = getattr(obj,field)
    
            td = "<td>%s</td>"%column_data
            tr += td
    
        return mark_safe(tr)
    my_func.py设置自定义函数

     Day3:对上面的功能添加分页,筛选,排序,搜索功能(功能之间的url需要重组)

    一:分页实现(在Django自带分页组件下进行扩展)

    from django.core.paginator import Paginator
    
    class CustomPagimator(Paginator):
        def __init__(self,current_page,max_page_num,*args,**kwargs):
            self.current_page = int(current_page)  #当前页
            self.max_page_num = max_page_num    #可以显示多少页
            super(CustomPagimator,self).__init__(*args,**kwargs)
    
        def page_num_range(self):
            # self.num_pages 总页数
            part_num = int(self.max_page_num/2)
            if self.num_pages <= self.max_page_num:
                return range(1, self.num_pages + 1)
            if self.current_page <= part_num:
                return range(1,self.max_page_num+1)
            elif self.current_page+part_num>= self.num_pages:
                return range(self.num_pages-self.max_page_num,self.num_pages+1)
            else:
                return range(self.current_page - part_num, self.current_page + part_num + 1)
    分页类代码
        current_page = request.GET.get('_p',1)
        paginator = CustomPagimator.CustomPagimator(current_page=current_page, max_page_num=3,object_list=querysets,per_page=2)  # 传入总数据和每页显示的数据
        try:
            filter_data = paginator.page(current_page)
        except PageNotAnInteger:
            filter_data = paginator.page(1)
        except EmptyPage:
            filter_data = paginator.page(paginator.num_pages)  # num_pages数总页数,最后一页
    
        page_html = paginator.page_num_range()
    分页类的使用
        <div>
            {% build_page_row queryset page_html admin_class %}
        </div>
    使用模板函数对分页数据进行url组合
    @register.simple_tag
    def build_page_row(queryset,page_html,admin_class):
        #先生成条件过滤数据
        filter_conditions = ''
        for k,v in admin_class.filter_conditions.items():
            filter_conditions += "&"+k+'='+v;
    
        #再生成排序条件
        if admin_class.sort_conditions:
            filter_conditions += "&o="+list(admin_class.sort_conditions.values())[0]
    
        #在生成搜索条件
        if admin_class.search_conditions:
            filter_conditions += "&_q="+admin_class.search_conditions
    
        page_str = '<ul class="pagination">'
        if queryset.has_previous():
            page_str += '<li><a href="?_p=%d%s">&laquo;</a></li>'%(queryset.previous_page_number(),filter_conditions)
        for i in page_html:
            if i == queryset.number:
                page_str += '<li class="active"><a href="?_p=%d%s">%d</a></li>'%(i,filter_conditions,i)
            else:
                page_str += '<li><a href="?_p=%d%s">%d</a></li>'%(i,filter_conditions,i)
        if queryset.has_next():
            page_str += '<li><a href="?_p=%d%s">&raquo;</a></li>'%(queryset.next_page_number(),filter_conditions)
    
        return mark_safe(page_str)
    build_page_row模板函数

    二:对各个字段筛选(对kingadmin中list_filter字段进行筛选)

    1:前端显示

        <div  class="form-group">
            <form method="get" class="form-inline">
            {% for field in admin_class.list_filter %}
                {% build_filter_row field admin_class %}
            {% endfor %}
            {% build_order_filter admin_class %}
            <button type="submit" class="btn btn-primary">提交</button>
            </form>
        </div>
    build_filter_row模板函数去获取数据生成标签

    2.模板函数去定制标签,在form表单中加入隐藏标签(表示排序和搜索条件)

    @register.simple_tag
    def build_filter_row(field,admin_class):
        model = admin_class.model
        field_obj = model._meta.get_field(field)
        filter_conditions = admin_class.filter_conditions
        label = """<label class="control-label">%s</label>"""%field
        try:
            select = "<div class='col-md-3'>%s:<select class='form-control' name='%s'>"%(label,field)
            data_list = field_obj.get_choices(field) #可以获取choices选项和外键
        except AttributeError:
            if field_obj.get_internal_type() in ("DateField","DateTimeField"):
                field = "%s__gte"%field
                select = "<div class='col-md-2'>%s:<select class='form-control' name='%s'>"%(label,field)
                time_now = datetime.now()
                time_list = [
                    ["", "---------"],
                    [time_now, "today"],
                    [time_now - timedelta(7), "七天内"],
                    [time_now.replace(day=1), "本月"],
                    [time_now - timedelta(90), "三个月内"],
                    [time_now.replace(month=1, day=1), "今年内"],
                    ['', "ALL"]
                ]
    
                def turn_date(date_list):
                    date_obj, date_str = date_list
                    if type(date_obj) is datetime:
                        date_obj = date_obj.strftime("%Y-%m-%d")
                    return (date_obj,date_str)
    
                data_list = map(turn_date,time_list)
            else:
                select = "<div class='col-md-3'>%s:<select class='form-control' name='%s'>"%(label,field)
                data_list = list(model.objects.values_list("id",field))
    
        for item in data_list:
            if str(item[0]) == filter_conditions.get(field,None):
                option = "<option value='%s' selected>" % str(item[0])
            else:
                option = "<option value='%s'>"%str(item[0])
            option += item[1]
            option += "</option>"
            select += option
    
        select += "</select></div>"
    
        return mark_safe(select)
    build_filter_row模板函数对日期筛选进行自定义,外键或者choices字段使用字段对象获取数据,对于其他的字段使用model获取所有的值,组成select框进行筛选

    3.在views中将url中的各个条件,放置到admin_class中,方便模板标签的使用

    @login_required
    def table_obj_list(request,app_name,model_name):
        '''取出指定的数据返给前端'''
    
        admin_class = site.enabled_admins[app_name][model_name]
    
        model_class = admin_class.model
        querysets = model_class.objects.all()   #所有数据
    
        #搜索后的数据
        querysets,search_conditions = get_search_result(request,querysets,admin_class)
        admin_class.search_conditions = search_conditions
    
        querysets,filter_conditions = get_filter_result(request,querysets)    #过滤条件后的数据
        admin_class.filter_conditions = filter_conditions   #也可以传值给前端,但是这样也不错
    
        querysets, sort_conditions = get_order_result(request,querysets,admin_class)
        admin_class.sort_conditions = sort_conditions
    
        current_page = request.GET.get('_p',1)
        paginator = CustomPagimator.CustomPagimator(current_page=current_page, max_page_num=3,object_list=querysets,per_page=2)  # 传入总数据和每页显示的数据
        try:
            filter_data = paginator.page(current_page)
        except PageNotAnInteger:
            filter_data = paginator.page(1)
        except EmptyPage:
            filter_data = paginator.page(paginator.num_pages)  # num_pages数总页数,最后一页
    
        page_html = paginator.page_num_range()
    
        return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class,"page_html":page_html})
    注意:我在views中将各个url条件放在admin_class中,方便查询对比(也可以放在变量中分发出来)

    4.在views中的url数据获取时将其他_q搜索,o排序,_p分页数据过滤,获取所有数据

    def get_filter_result(request,querysets):
        filter_conditions = {}
        for k,v in request.GET.items():
            if k in ("_p","o","_q"):continue
            if v:
                filter_conditions[k] = v
        return querysets.filter(**filter_conditions),filter_conditions
    get_filter_result过滤条件,获取querysets数据

    推文:python---Django中模型类中Meta元对象了解,可以知道数据模型中的字段对象或者其他所需要的内容

     三:对各个字段进行排序(list_display)

    1.前端传递排序数据,对于table中的th加上url

                <thead>
                    <tr>
                        {% build_title_row admin_class %}
                    </tr>
                </thead>
    使用模板函数处理

    2.模板函数build_title_row 去生成标签

    @register.simple_tag
    def build_title_row(admin_class):
        #先生成过滤条件
        filter_conditions = ''
        for k, v in admin_class.filter_conditions.items():
            filter_conditions += "&" + k + '=' + v;
    
        #再生成搜索条件
        if admin_class.search_conditions:
            filter_conditions += "&_q="+admin_class.search_conditions
    
        icon = """<span class="glyphicon glyphicon-triangle-%s" aria-hidden="true"></span>"""
        title = ''
        th = "<th><a href='?o=%s%s'>%s%s</a></th>"
    
        try:
            sort_cond = list(admin_class.sort_conditions.keys())[0]
            sort_val = list(admin_class.sort_conditions.values())[0]
        except IndexError:
            sort_cond = None
            sort_val = None
        for counter,field in enumerate(admin_class.list_display):
            if field == sort_cond:
                if sort_val.startswith("-"):
                    title += th%(sort_val.strip("-"),filter_conditions,field,icon%"top")
                else:
                    title += th%("-"+sort_val,filter_conditions,field,icon%"bottom")
            else:
                title += th%(counter,filter_conditions,field,"")
    
        return mark_safe(title)
    build_title_row中先将过滤和搜索条件组合,再生成排序url(符号倒序,数字代表在admin_class中list_display字段中的索引顺序)

    3.views中对我们获取的所有数据,根据前端传递的排序方法进行排序处理

    def get_order_result(request,querysets,admin_class):
        order_index = request.GET.get("o")
        sort_conditions = {}
        if order_index:
            index = abs(int(order_index))
            order_by = admin_class.list_display[index]
            if order_index.startswith("-"):
                querysets = querysets.order_by("-"+order_by)
            else:
                querysets = querysets.order_by(order_by)
            sort_conditions[order_by] = order_index
    
        return querysets,sort_conditions
    get_order_result获取排序结果

    四:对字段进行搜索(search_fields)

    1.前端生成标签时,form表单中需要一起传递其他条件的input隐藏框

        <form class="form-horizontal" method="get" role="form">
              <div class="form-group">
                <div class="col-sm-8">
                  <input class="form-control" id="focusedInput" name="_q" placeholder="输入数据开始搜索...." value="{{ admin_class.search_conditions }}" type="text">
                  {% build_search_filter admin_class %}
                </div>
                  <div class="col-sm-2">
                      <input type="submit" class="btn btn-primary" value="Search">
                  </div>
              </div>
        </form>
    前端数据form

    2.使用模板函数生成标签

    @register.simple_tag
    def build_search_filter(admin_class):
        '''向搜索框中添加入过滤条件和排序条件'''
        # 先生成过滤条件
        inp = ""
        for k, v in admin_class.filter_conditions.items():
            inp += """<input type="hidden" name="%s" value="%s"/>"""%(k,v)
    
        # 再生成排序条件
        if admin_class.sort_conditions:
            inp += """<input type="hidden" name="%s" value="%s"/>""" % ("o", list(admin_class.sort_conditions.values())[0])
    
        return mark_safe(inp)
    build_search_filter模板函数,生成input标签(含有各个条件)

    3.后端处理搜索条件,生成querysets数据

    def get_search_result(request,querysets,admin_class):
        search_val = request.GET.get("_q")
        if search_val:
            q = Q()
            q.connector = "OR"
            for field in admin_class.search_fields:
                q.children.append(("%s__contains"%field,search_val))
            querysets = querysets.filter(q)
        return querysets,search_val
    views处理搜索字段,注意使用OR,需要用到Q方法

     推文:python---django中orm的使用(2)

    Day4:动态生成任意表的CURD

    1.如何在前端动态生成标签?使用form验证可以针对model生成所有的字段控件

    推文:python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类

    from django.forms import ModelForm
    from repository import models
    
    class CustomerForm(ModelForm):
        class Meta:
            model = models.CustumerInfo #将表与元类中的数据关联
            fields = "__all__"
    
        def __new__(cls, *args, **kwargs):
            print(cls.base_fields)
            #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
            #这张表中的所有字段对象
            for field_name,field_obj in dict(cls.base_fields).items():
                field_obj.widget.attrs.update({'class':"form-control"})
    
            return ModelForm.__new__(cls)
    实验:使用固定的数据模型去生成对应的form验证类,可以用来在前端之间生成控件

    2.如何针对每张表动态生成一个Form类?需要用到type方法去动态生成类

    推文:python---基础知识回顾(三)(面向对象)

    from django.forms import ModelForm
    from repository import models
    
    def create_dynamic_model_form(admin_class,form_add = False):
        '''动态生成modelform,form_add表示是添加数据生成form类。添加和编辑有所区别'''
        class Meta:
            model = admin_class.model # 将表与元类中的数据关联
            fields = "__all__"
            if not form_add:
                exclude = admin_class.readonly_fields
                admin_class.add_flag = False
            else:
                exclude = []
                admin_class.add_flag = True
    
    
        def __new__(cls, *args, **kwargs):
            #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
            #这张表中的所有字段对象
            for field_name,field_obj in dict(cls.base_fields).items():
                field_obj.widget.attrs.update({'class':"form-control"})
                # if field_name in admin_class.readonly_fields:
                #     field_obj.widget.attrs.update({'disabled':'true'})
            return ModelForm.__new__(cls)
    
        dynamic_form = type("DynamicModelForm",(ModelForm,),{'Meta':Meta,"__new__":__new__})
    
        return dynamic_form
    form_handle.py中去创建方法,动态创建类

    3.在修改页面中动态创建Form类(需要传递原来数据)

    @login_required
    def table_obj_change(request,app_name,model_name,obj_id):
        '''kingadmin数据修改页面'''
        admin_class = site.enabled_admins[app_name][model_name]
    
        #动态生成form表单
        model_form = form_handle.create_dynamic_model_form(admin_class)
        obj = admin_class.model.objects.get(id=obj_id)
    
        if request.method == "GET":
            form_obj = model_form(instance=obj)
        elif request.method == "POST":
            form_obj = model_form(instance=obj,data=request.POST)
            if form_obj.is_valid():
                form_obj.save()
                return redirect("/kingadmin/%s/%s"%(app_name,model_name))
    
        return render(request,"kingadmin/table_obj_change.html",locals())
    table_obj_change方法去创建form类,传递到前端
        url(r"^(w+)/(w+)/(d+)/change/$", views.table_obj_change, name="table_obj_change"),
    url中对于修改的匹配

     

    4.在添加页面动态创建Form类

    @login_required
    def table_obj_add(request,app_name,model_name):
        admin_class = site.enabled_admins[app_name][model_name]
    
        model_form = form_handle.create_dynamic_model_form(admin_class,form_add = True)
    
        if request.method == "GET":
            form_obj = model_form()
        elif request.method == "POST":
            form_obj = model_form(data=request.POST)
            if form_obj.is_valid:
                form_obj.save()
                return redirect("/kingadmin/%s/%s" % (app_name, model_name))
    
        return render(request,"kingadmin/table_obj_add.html",locals())
    table_obj_add方法
        url(r"^(w+)/(w+)/add/$", views.table_obj_add, name="table_obj_add")
    url.py中对于添加的匹配

    添加的url

    5.修改和添加的HTML和公共部分

    {% extends "kingadmin/index.html" %}
    {% load my_func %}
    
    
    {% block right-content-container %}
    <h2 class="page-header">{% get_model_name admin_class %}</h2>
    <h3 class="page-header">添加{% get_model_name admin_class %}</h3>
        <div>
        add
            {% include "kingadmin/table_obj_change_component.html" %}
        </div>
    {% endblock %}
    table_obj_add.html
    {% extends "kingadmin/index.html" %}
    {% load my_func %}
    
    
    {% block right-content-container %}
    <h2 class="page-header">{% get_model_name admin_class %}</h2>
    <h3 class="page-header">修改{{ form_obj.instance }}</h3>
        <div>
        change
                {% include "kingadmin/table_obj_change_component.html" %}
        </div>
    {% endblock %}
    table_obj_change.html
    {% load my_func %}
    
    <form class="form-horizontal" onsubmit="ChangeSelStatus(this);" method="post" role="form">
                {% csrf_token %}
                {% for field in form_obj %}
                      <div class="form-group">
                         <label class="col-sm-2 control-label">{{ field.label }}</label>
                        {% if field.name in admin_class.filter_horizontal %}
                            <div class="col-sm-5">
                                <select class="form-control" name="" id="id_{{ field.name }}_from" multiple>
                                    {% get_rel_m2m_val field.name admin_class as rel_querysets %}
                                    {% for rel_obj in rel_querysets %}
                                        {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%}
                                        {% if not sel_flag %}
                                        <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>
                                        {% endif %}
                                    {% endfor %}
                                </select>
                            </div>
                            <div class="col-sm-5">
                                <select tag="submit" class="form-control" name="{{ field.name }}" id="id_{{ field.name }}_to" multiple>
                                    {% get_rel_m2m_val field.name admin_class as rel_querysets %}
                                    {% for rel_obj in rel_querysets %}
                                        {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%}
                                        {% if sel_flag %}
                                        <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>
                                        {% endif %}
                                    {% endfor %}
                                </select>
                            </div>
                        {% else %}
                        <div class="col-sm-10">
                          {{ field }}
                            <span style="color: red;">
                                {{ field.errors.0 }}
                            </span>
                        </div>
                        {% endif %}
                      </div>
                {% endfor %}
                {% if not admin_class.add_flag %}
                    {% for field_name in admin_class.readonly_fields %}
                         <div class="form-group">
                            <label class="col-sm-2 control-label">{{ field_name }}</label>
                            <div class="col-sm-10">
                                    <p class="text-left">{% get_field_value_p field_name form_obj %}</p>
                            </div>
                         </div>
                    {% endfor %}
                {% endif %}
                <div class="col-lg-offset-11 col-sm-1">
                    <input type="submit" class="btn btn-success" value="Save">
                </div>
                </form>
    
    {% block extra-js %}
        <script>
        function MoveEleToOpp(ths,field_name) {
            if($(ths).parent().prop("id") == "id_"+field_name+"_from"){
                var new_target = "id_"+field_name+"_to";
            }else{
                var new_target = "id_"+field_name+"_from";
            }
            $("#"+new_target).append(ths);
        }
    
        function ChangeSelStatus(ths){
            $("select[tag] option").prop("selected",true);
        }
        </script>
    {% endblock %}
    table_obj_change_component.html公共部分

     6.处理在add和change中对于readonly_fileds字段的不同

                {% if not admin_class.add_flag %}
                    {% for field_name in admin_class.readonly_fields %}
                         <div class="form-group">
                            <label class="col-sm-2 control-label">{{ field_name }}</label>
                            <div class="col-sm-10">
                                    <p class="text-left">{% get_field_value_p field_name form_obj %}</p>
                            </div>
                         </div>
                    {% endfor %}
                {% endif %}
    在动态生成ModelForm修改,并且向admin_class.add_flag加入标识,前端进行判别,决定是否去显示只读字段

    7.对于filter_horizontal字段我们在模板函数中进行获取所有的值,并且判断是否显示在哪一个select标签中

                            <div class="col-sm-5">
                                <select class="form-control" name="" id="id_{{ field.name }}_from" multiple>
                                    {% get_rel_m2m_val field.name admin_class as rel_querysets %}
                                    {% for rel_obj in rel_querysets %}
                                        {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%}
                                        {% if not sel_flag %}
                                        <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>
                                        {% endif %}
                                    {% endfor %}
                                </select>
                            </div>
                            <div class="col-sm-5">
                                <select tag="submit" class="form-control" name="{{ field.name }}" id="id_{{ field.name }}_to" multiple>
                                    {% get_rel_m2m_val field.name admin_class as rel_querysets %}
                                    {% for rel_obj in rel_querysets %}
                                        {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%}
                                        {% if sel_flag %}
                                        <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>
                                        {% endif %}
                                    {% endfor %}
                                </select>
                            </div>
    前端对filter_horizontal进行判别,针对两个select都进行判别,一个放置选中一个放置未选中
    @register.simple_tag
    def get_rel_m2m_val(field_name,admin_class):
        field_obj = admin_class.model._meta.get_field(field_name)
        rel_model = field_obj.related_model
        querysets = rel_model.objects.all()
        return querysets
    get_rel_m2m_val模板函数获取关联对象得所有值,用到字段对象的related_model属性获取关联对象
    @register.simple_tag
    def get_rel_m2m_sel(form_obj,field_name,rel_obj):
        try:
            querysets = getattr(form_obj.instance, field_name).all()
            if rel_obj in querysets:
                return True
            return False
        except TypeError:
            return False
    get_rel_m2m_sel方法判断是否数据被选中,返回True选中,放在第二个select标签,放在未选中,放在第一个select标签

    8.实现js双击option,在两个select之间跳转

               

    <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>
    为两个select标签绑定同一个MoveEleToOpp方法
        function MoveEleToOpp(ths,field_name) {
            if($(ths).parent().prop("id") == "id_"+field_name+"_from"){
                var new_target = "id_"+field_name+"_to";
            }else{
                var new_target = "id_"+field_name+"_from";
            }
            $("#"+new_target).append(ths);
        }
    MoveEleToOpp方法实现:通过判断父标签select的id,将当前option转移append到对方的select中

    9.实现在点击保存时,form表单自动将右侧select中的数据全部选中。注意:加上name为select标签,name="字段名"

    <form class="form-horizontal" onsubmit="ChangeSelStatus(this);" method="post" role="form">
    为form表单绑定方法ChangeSelStatus
        function ChangeSelStatus(ths){
            $("select[tag] option").prop("selected",true);
        }
    ChangeSelStatus实现

     10.为filter_horizontal完善功能,添加全选,全部移除

                                <p><a onclick="ChooseAll(this,'{{ field.name }}')">ChooseAll</a></p>
                                <p><a onclick="ChooseAll(this,'{{ field.name }}')">RemoveAll</a></p>
    前端HTML
        function ChooseAll(ths,field_name) {
            var sel_id = $(ths).parent().prev().prop("id")
            if(sel_id == "id_"+field_name+"_from"){
                var new_target = "id_"+field_name+"_to";
            }else{
                var new_target = "id_"+field_name+"_from";
            }
    
            $("#"+sel_id).find("option").each(function(){
                $("#"+new_target).append(this);
            })
        }
    ChooseAll函数js代码

    Day5:删除功能开发和action方法实现

    1.删除功能开发

    {% extends "kingadmin/index.html" %}
    {% load my_func %}
    
    
    {% block right-content-container %}
    <h2 class="page-header">{% get_model_name admin_class %}</h2>
    <h3 class="page-header alert-danger">注意:以下与{{ obj }}想关联的数据都将被删除</h3>
        <div>
        {% get_del_obj obj app_name model_name as res_del %}
        {{ res_del|safe }}
        </div>
    
        <form method="post">
        {% csrf_token %}
            <input type="submit" class="btn btn-danger" value="确认删除">
            <a href="/kingadmin/{{ app_name }}/{{ model_name }}/{{ obj.id }}/change" class="btn btn-primary">返回</a>
        </form>
    {% endblock %}
    前端HTML代码
    @register.simple_tag
    def get_del_obj(model_obj, app_name, model_name):
        all_rel = model_obj._meta.related_objects
        ul = "<ul>"
        ul += "<li>%s:<a href='/kingadmin/%s/%s/%s/change'>%s</a></li>"%(model_obj._meta.label.rsplit('.',maxsplit=1)[1],app_name,model_name,model_obj.id,model_obj)
        for rel_field in all_rel:
            sub_querysets = getattr(model_obj,rel_field.name+"_set").all()
            if not sub_querysets:   #若是关联但是没有数据,则不显示
                continue
            if rel_field.get_internal_type() == "ManyToManyField":
                ul += "<li><ul>"
                ul += "<li>%s</li>"%rel_field.name
                for i in sub_querysets:
                    ul += "<li><ul><li>%s</li></ul></li>" % i
                ul += "</ul></li>"
            else:
                for sub_item in sub_querysets:
                    sub_res = get_del_obj(sub_item,sub_item._meta.app_label,sub_item._meta.model_name)
                    ul += "<li>%s</li>"%(sub_res)
    
        ul += "</ul>"
    
        return ul
    模板函数,去递归生成标签
    @login_required
    def table_obj_delete(request,app_name,model_name,obj_id):
        admin_class = site.enabled_admins[app_name][model_name]
        obj = admin_class.model.objects.get(id=obj_id)
    
        if request.method == "POST":
            obj.delete()
            return redirect("/kingadmin/{app_name}/{model_name}".format(app_name=app_name,model_name=model_name))
    
        return render(request, "kingadmin/table_obj_delete.html", locals())
    views后台删除代码

    2.action字段功能完善

    class CustomerAdmin(BaseKingAdmin):
        list_display = ['id','name','contact_type','contact','source','consult_content','consultant','status','date']
        list_filter = ['source','consultant','status','date']
        search_fields = ['contact','consultant__name','name']
        readonly_fields = ['status','contact']
        filter_horizontal = ['consult_courses',]
        action = ['change_status',]
        def change_status(self,request,querysets):
            querysets.update(status=1)
    kingadmin.py中放置action字段,包含有自定义方法
    from django.shortcuts import render
    
    class BaseKingAdmin(object):
        list_display = []
        list_filter = []
        search_fields = []
        readonly_fields = []
        filter_horizontal = []
        action = []
        def_action = ['delete_selected_objs']
    
        def delete_selected_objs(self,request,querysets):
    
            return render(request,'kingadmin/table_obj_delete.html')
    
        def __init__(self):
            self.action.extend(self.def_action)
    admin_base.py中需要去设置action默认数据

    (1)设置form表单布局

        <div>
            <form action="" method="post" class="form-inline" onsubmit="return Raw_input_action(this);">
            <div class="col-lg-3">
            {% csrf_token %}
            <label class="control-label">Action:</label>
                <select name="action" id="action" class="form-control">
                <option value="">---------</option>
                {% for action in admin_class.action %}
                    <option value="{{ forloop.counter0 }}">{{ action }}</option>
                {% endfor %}
            </select>
            </div>
            <div class="col-sm-2">
                <input type="submit" value="Go" class="btn btn-primary">
            </div>
            </form>
        </div>
    form表单

    (2)设置复选框完成全选功能

        <div>
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th><input type="checkbox" id="check_All" onclick="CheckAll(this)"/></th>
                        {% build_title_row admin_class %}
                    </tr>
                </thead>
                <tbody>
                    {% for item in queryset %}
                        <tr>
                        <td><input type="checkbox" name="check_row" value="{{ item.id }}"></td>
                        {% build_table_row item admin_class %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    HTML代码
        function CheckAll(ths) {
            if($(ths).prop("checked")){
                $("input[name=check_row]").prop("checked",true)
            }else{
                $("input[name=check_row]").prop("checked",false)
            }
        }
    CheckAll方法js完成全选

    (3)提交表单前先生成隐藏表单去获取数据集

        function Raw_input_action(ths) {
            if($("#action").val() == ""){
                alert("请选择正确的action");
                return false;
            }
            var select_ids = [];
            $("input[name=check_row]").filter(":checked").each(function(){
                select_ids.push($(this).val());
            })
            if(select_ids.length == 0){
                alert("请选择正确的项目");
                return false;
            }
            new_ele = "<input type='hidden' name='select_ids' value='"+JSON.stringify(select_ids)+"'/>";
            $(ths).append(new_ele);
            return true;
        }
    Raw_input_action方法生成一个人input标签

    (4)传递到后端进行处理

    @login_required
    def table_obj_list(request,app_name,model_name):
        '''取出指定的数据返给前端'''
    
        admin_class = site.enabled_admins[app_name][model_name]
    
        model_class = admin_class.model
        querysets = model_class.objects.all()   #所有数据
    
        if request.method == "POST":
            selected_action = request.POST.get("action")
            selected_ids = request.POST.get("select_ids")
            getattr(admin_class,admin_class.action[int(selected_action)])(request,querysets.filter(id__in=json.loads(selected_ids)))
    在table_obj_list方法添加上post方法即可

     3.处理action中的默认行为delete批量删除

    (1)提交的url不是上面的table_obj_delete,而是本页面和change_status一起作为action传递入当前url

        def delete_selected_objs(self,request,querysets):
            return render(request,'kingadmin/table_obj_delete.html',{"admin_class":self,'obj':querysets})
    delete_selected_objs的action方法

    (2)获取delete_selected_objs在table_obj_list方法中返回

        if request.method == "POST":
            if request.POST.get("delete_ids"):
                '''如果是删除做post传递过来的话另外处理,否则就是action操作'''
                del_id = json.loads(request.POST.get("delete_ids"))
                admin_class.model.objects.filter(id__in=del_id).delete()
                return redirect("/kingadmin/%s/%s"%(app_name,model_name))
            else:
                selected_action = request.POST.get("action")
                selected_ids = request.POST.get("select_ids")
                res = getattr(admin_class,admin_class.action[int(selected_action)])(request,querysets.filter(id__in=json.loads(selected_ids)))
                #如果返回值,代表是返回render指向delete页面
                if res:
                    return res
    table_obj_list中对于post的处理

    若是执行完action方法后没有返回值则是正常执行,如果有返回值,则是代表我们接下来是执行删除操作。需要返回

    (3)我们还是调用的上面的table_obj_delete.html页面,但是其中的模板标签函数,是针对一个数据对象,而现在是一个数据集,我们需要再次处理

    @register.simple_tag
    def get_del_obj(model_objs, app_name, model_name):
        ul = "<ul>"
    
        try:
            iter(model_objs)
        except TypeError:
            model_objs = [model_objs,]
    
        for model_obj in model_objs:
            all_rel = model_obj._meta.related_objects
            ul += "<li>%s:<a href='/kingadmin/%s/%s/%s/change'>%s</a></li>"%(model_obj._meta.label.rsplit('.',maxsplit=1)[1],app_name,model_name,model_obj.id,model_obj)
            for rel_field in all_rel:
                sub_querysets = getattr(model_obj,rel_field.name+"_set").all()
                if not sub_querysets:   #若是关联但是没有数据,则不显示
                    continue
                if rel_field.get_internal_type() == "ManyToManyField":
                    ul += "<li><ul>"
                    ul += "<li>%s</li>"%rel_field.name
                    for i in sub_querysets:
                        ul += "<li><ul><li>%s</li></ul></li>" % i
                    ul += "</ul></li>"
                else:
                    for sub_item in sub_querysets:
                        sub_res = get_del_obj(sub_item,sub_item._meta.app_label,sub_item._meta.model_name)
                        ul += "<li>%s</li>"%(sub_res)
    
        ul += "</ul>"
    
        return ul
    简单改变模板函数get_del_obj,将原来单个对象也改写为可迭代

    (4)我们提交数据,也不再是table_obj_delete方法,而是table_obj_list方法,所以我们需要传递一个数据代表要删除的数据id集合,同时一个一个标识

    <input type="hidden" name="delete_ids" value="{% get_del_objs_id obj %}">
    在显示的table_obj_delete.html页面加入隐藏标签,收集所有id集合
    @register.simple_tag
    def get_del_objs_id(objs):
        obj_ser = []
        for obj in objs:
            obj_ser.append(obj.id)
        return json.dumps(obj_ser)
    get_del_objs_id模板函数收集所有的数据对象的id,json序列化返回给前端

    (5)views页面根据post传递过来的隐藏标签的name,判断是不是执行删除数据操作

        if request.method == "POST":
            if request.POST.get("delete_ids"):
                '''如果是删除做post传递过来的话另外处理,否则就是action操作'''
                del_id = json.loads(request.POST.get("delete_ids"))
                admin_class.model.objects.filter(id__in=del_id).delete()
                return redirect("/kingadmin/%s/%s"%(app_name,model_name))
    获取前端input表单名delete_ids,判断是否有数据,来决定是否删除

     4.实现面包屑导航

     

     

        <ol class="breadcrumb">
            <li><a href="/kingadmin">CRM</a></li>
            <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
            <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li>
            <li class="active">{% get_nva_active admin_class %}</li>
        </ol>
    add页面导航
        <ol class="breadcrumb">
            <li><a href="/kingadmin">CRM</a></li>
            <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
            <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li>
            <li class="active">{% get_nva_active admin_class %}</li>
        </ol>
    list页面导航
        <ol class="breadcrumb">
            <li><a href="/kingadmin">CRM</a></li>
            <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
            <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li>
            <li class="active">{{ form_obj.instance }}</li>
        </ol>
    change页面导航
    @register.simple_tag
    def get_nva_active(admin_class):
        return admin_class.model._meta.verbose_name
    get_nva_active模板函数获取对象的中文名
        <ol class="breadcrumb">
            <li><a href="/kingadmin">CRM</a></li>
            <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
            <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li>
            <li class="active">{% get_nav_del obj %}</li>
        </ol>
    delete页面导航
    @register.simple_tag
    def get_nav_del(model_objs):
        obj_names = []
        try:
            iter(model_objs)
        except TypeError:
            model_objs = [model_objs,]
        for model in model_objs:
            obj_names.append("%s"%model)
        return '|'.join(obj_names)
    get_nav_del模板函数组合对象名

     5.左侧菜单状态

                   {% for menu in role.menus.select_related %}
                       {% if menu.url_type == 0 %}
                           {% if menu.url_name == request.path %}
                               <li class="active"><a href="{{ menu.url_name }}">{{ menu.name }}</a></li>
                           {% else %}
                               <li><a href="{{ menu.url_name }}">{{ menu.name }}</a></li>
                           {% endif %}
                       {% else %}
                           {% url menu.url_name as url_name %}
                           {% if url_name == request.path %}
                               <li class="active"><a href="{{ url_name }}">{{ menu.name }}</a></li>
                           {% else %}
                               <li><a href="{{ url_name }}">{{ menu.name }}</a></li>
                           {% endif %}
                       {% endif %}
                   {% endfor %}
    index页面在生成url时,对其进行判断。要分辨动态和绝对

     Day6:学员报名流程开发

    class ContractTemplate(models.Model):
        '''合同模板表'''
        name = models.CharField(max_length=64)
        content = models.TextField()
        date = models.DateField(auto_now_add=True)
    
        def __str__(self):
            return self.name
    
    class StudentEnrollment(models.Model):
        '''学员报名表:这里还没有变成学员,适合客户表相关联'''
        customer = models.ForeignKey("CustumerInfo")
        class_grade = models.ForeignKey("ClassList")
        consultant = models.ForeignKey("UserProfile")   #对应的销售
        contract_agreed = models.BooleanField(default=False)    #是否同意合同
        contract_signed_date = models.DateTimeField(blank=True,null=True)   #同意合同未到时间
        contract_approved = models.BooleanField(default=False)  #审核是否完毕
        contract_approved_date = models.DateTimeField(blank=True,null=True)
    
        class Meta:
            unique_together = ("customer","class_grade")
    
        def __str__(self):
            return "%s"%self.customer
    
    
    class PaymentRecord(models.Model):
        '''存储学员缴费记录'''
        enrollment = models.ForeignKey("StudentEnrollment")
        payment_type_choice = (
            (0,"报名费"),
            (1,"学费"),
            (2,"退费"),
        )
        payment_type = models.SmallIntegerField(choices=payment_type_choice)
        amount = models.IntegerField("费用",default=500)
        consultant = models.ForeignKey("UserProfile")   #费用缴给谁
        date = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return "%s"%self.enrollment
    新增3张表:学员注册表,合同表(和班级关联),缴费记录表

    一:销售为想报名的学员提供链接

    @login_required
    def Student_encroll(request):
        ClassList = models.ClassList.objects.all()
        StuEncroll = models.CustumerInfo.objects.filter(consultant__user=request.user).all()
    
        if request.method == "POST":
            student = request.POST.get("student")
            classlist = request.POST.get("classlist")
    
            try:
                StuEncObj = models.StudentEnrollment.objects.create(
                    customer_id=student,
                    class_grade_id=classlist,
                    consultant=request.user.userprofile
                )
            except IntegrityError:
                StuEncObj = models.StudentEnrollment.objects.get(
                    customer_id=student,
                    class_grade_id=classlist,
                    consultant=request.user.userprofile
                )
                if StuEncObj.contract_agreed:
                    return redirect("encrollment/%s/contract_audit.html"%StuEncObj.id)
                else:
                    return HttpResponse("等待学员身份验证")
    
            link = "http://127.0.0.1:8000/sale/encrollment/%s.html"%StuEncObj.id
        return render(request,"sale/stu_encroll.html",locals())
    Student_encroll学员注册链接获取

    二:学员获取链接,进行填写信息,查阅合同,同意并上传证件信息

    def enrollment(request,id):
        '''学员在线报名'''
        enrollment_obj = models.StudentEnrollment.objects.get(id=id)
    
        if enrollment_obj.contract_agreed and not enrollment_obj.contract_approved:
            return HttpResponse("信息正在审核当中")
    
        if enrollment_obj.contract_approved:
            return HttpResponse("审核通过,去进行缴费操作")
    
        if request.method == "GET":
            forms = CustomerForm(instance=enrollment_obj.customer)
        elif request.method == "POST":
            if not request.POST.get("contract_agreed"):
                return HttpResponse("信息提交失败,请先阅读合同")
            cus_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR, id)
            if len(os.listdir(cus_dir)) == 0:
                return HttpResponse("信息提交失败,请先上传证件信息")
    
            forms = CustomerForm(instance=enrollment_obj.customer,data=request.POST)
            if forms.is_valid():
                forms.save()
                enrollment_obj.contract_agreed = True
                enrollment_obj.contract_signed_date = datetime.datetime.now()
                enrollment_obj.save()
                return HttpResponse("信息提交成功")
        file_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR,id)
        if os.path.isdir(file_dir):
            file_info = os.listdir(file_dir)
    
        return render(request,"sale/enrollment.html",locals())
    enrollment学员在线报名
    {% extends "index.html" %}
    
    {% block extra-link %}
    <link rel="stylesheet" href="/static/plugins/dropzone/dropzone.css">
    {% endblock %}
    
    {% block body %}
    <h1 class="page-header">学员报名</h1>
    
    <div class="col-lg-offset-1 col-lg-10">
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">学员在线报名</h3>
        </div>
        <div class="panel-body">
            <form class="form-horizontal" onsubmit="return PrevSubmit(this);" method="post" role="form">
                {% csrf_token %}
                {% for field in forms %}
                  <div class="form-group col-sm-6">
                    <label class="col-sm-4 control-label">{{ field.label }}</label>
                    <div class="col-sm-8">
                      {{ field }}
                        <span style="color: red;">{{ field.errors.0 }}</span>
                    </div>
                  </div>
                {% endfor %}
                  <div class="form-group col-sm-6">
                    <label class="col-sm-4 control-label">报名班级</label>
                    <div class="col-sm-8">
                      <p class="form-control-static">{{ enrollment_obj.class_grade }}</p>
                    </div>
                  </div>
                  <div class="form-group col-sm-6">
                    <label class="col-sm-4 control-label">学费</label>
                    <div class="col-sm-8">
                      <p class="form-control-static">{{ enrollment_obj.class_grade.couser.price }}</p>
                    </div>
                  </div>
                  <div class="col-sm-12">
                      <pre style="height: 400px">
                        {{ enrollment_obj.class_grade.contract_template.content }}
                      </pre>
                      <div>
                      <div class="form-inline">
                          <label class="col-sm-6 control-label" style="padding-top: 0px">是否同意以上合同</label>
                          <input type="checkbox" value="1" name="contract_agreed">
                      </div>
                    </div>
                  </div>
                  <div class="form-group col-sm-6">
                    <div class="col-sm-8">
                        <input type="submit" class="btn btn-success" value="提交">
                    </div>
                  </div>
            </form>
          <div class="col-sm-12">
              <ul id="file_ul" style="list-style: none">
              <label class="control-label" style="padding-top: 0px;">已上传的文件目录</label>
                  {% for file in file_info %}
                    <li>{{ file }}</li>
                  {% endfor %}
              </ul>
            <form action="{% url 'enrollment_fileupload' enrollment_obj.id %}" id="myAwesomeDropzone" class="dropzone">
              <div class="fallback">
                <input name="file" type="file" multiple />
              </div>
            </form>
          </div>
        </div>
    
    </div>
    </div>
    
    
    
    {% endblock %}
    
    {% block extra-js %}
    <script src="/static/plugins/dropzone/dropzone.js"></script>
    <script>
    $(function () {
        Dropzone.options.myAwesomeDropzone = {
          paramName: "file", // The name that will be used to transfer the file
          maxFilesize: 2, // MB
          maxFiles:2,
          parallelChunkUploads:true,
          accept: function(file, done) {
            if (file.name == "justinbieber.jpg") {
              done("Naha, you don't.");
            }
            else { done(); }
          },
          init: function() {
            this.on("success", function(file,respone) {
                /* Maybe display some more file information on your page */
                var rep = JSON.parse(respone)
                if(!rep.status){
                    alert(rep.message);
                    return;
                }else{
                    var li = "<li>"+file.name+"</li>";
                    $("#file_ul").append(li);
                }
            });
          }
        };
    });
    
    function PrevSubmit(ths){
        if(!$($(ths).find(":checkbox[name=contract_agreed]")[0]).prop("checked")){
            alert("请先阅读合同");
            return false;
        }
    
        if($("#file_ul").find("li").length==0){
            alert("请先上传证件信息");
            return false;
        }
    
        $(ths).find(":disabled").removeAttr("disabled")
        return true
    }
    </script>
    
    {% endblock %}
    erollment.html报名页面,含有Dropzone使用
    @csrf_exempt
    def enrollment_fileupload(request,encrollment_obj_id):
        cus_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR,encrollment_obj_id)
        if not os.path.isdir(cus_dir):
            os.makedirs(cus_dir)
    
        status = {
            'status':True,
            "message":None
        }
        print(request.FILES)    #需要去接收文件,前端状态才会是true
        if len(os.listdir(cus_dir)) >= 2:
            status['status'] = False
            status['message'] = "文件超出上传个数"
            return HttpResponse(json.dumps(status))
    
        file_obj = request.FILES.get("file")
    
        with open(os.path.join(cus_dir,file_obj.name),"wb") as fp:
            for chunks in file_obj.chunks():
                fp.write(chunks)
    
        return HttpResponse(json.dumps(status))
    enrollment_fileupload处理Dropzone文件传输

    三:销售审核学员注册信息,审核通过,为其生成账号(密码需要使用Django模块加密),发送邮件

    from django.contrib.auth.hashers import make_password  #用于生成密码
    @login_required
    def contract_audit(request,id):
        '''合同审查'''
        enrollment_obj = models.StudentEnrollment.objects.get(id=id)
    
        if not enrollment_obj.contract_agreed:
            return redirect("/sale/EncrollLink.html")
    
        if enrollment_obj.contract_approved:
            return HttpResponse("审核通过,等待缴费")
    
        if request.method == "GET":
            forms = CustomerForm(instance=enrollment_obj.customer)
            contract_forms = ContractForm(instance=enrollment_obj)
        elif request.method == "POST":
            forms = CustomerForm(instance=enrollment_obj.customer,data=request.POST)
            contract_forms = ContractForm(instance=enrollment_obj,data=request.POST)
            if forms.is_valid() and contract_forms.is_valid():
                forms.save()
                contract_forms.save()
                enrollment_obj.contract_approved_date = datetime.datetime.now()
                enrollment_obj.save()
    
                try:
                    stu_obj = enrollment_obj.customer.student
                except Exception:
                    pass
                else:
                    return HttpResponse(
                        "用户%s已添加--->账号为:%s" % (enrollment_obj.customer.name, enrollment_obj.customer.student.account.name))
    
                #生成一个随机字符串
                account = ''.join(random.sample(string.digits,8))
                pwd = ''.join(random.sample(string.ascii_letters+string.digits,8))
    
                #创建一个账号
                user = models.User.objects.create(
                    username=account,
                    password=make_password(pwd),
                )
    
                #创建一个用户
                userprofile = models.UserProfile.objects.create(
                    user=user,
                    name=enrollment_obj.customer.name,
                )
    
                #为用户绑定一个角色
                userprofile.role.add(models.Role.objects.get(name="Student"))
    
                #保存到学员表
                models.Student.objects.create(
                    customer=enrollment_obj.customer,
                    class_grades=enrollment_obj.class_grade,
                    account=userprofile,
                )
    
                send_info = "账号:%s
    密码:%s"%(account,pwd)
    
                send_mail(
                    '成功成为正式学员',
                    send_info,
                    '18904190363@sina.cn',
                    ["%s@qq.com"%enrollment_obj.customer.contact,],
                    fail_silently=False,
                )
    
                return HttpResponse("审核通过,等待缴费")
            print(forms.errors,contract_forms.errors)
    
        return render(request,"sale/contract_audit.html",locals())
    contract_audit合同审核后生成账号,发送信息

    Django自带邮件发送模块

    (1)settings中配置

    (2)导入模块发送邮件

     

    day7:实现讲师和学员作业发布上传功能(其中由于多使用table浏览数据,可以自定义一个类似于form的基类,去统一实现table显示)

    一:讲师功能

    (1)可以看出上面多是table显示信息,下面自定义table_form类似于forms

    import re
    
    class BaseForm(object):
        display_list = []
        field_tag = {}
        attrs = {}
        extra_field = []
    
        def __init__(self,model,querysets):
            self.instance = model
            self.querysets = querysets
            self.th = []
            self.tr = []
    
    
        def register(self):
            for field in self.display_list:
                if field == "self":
                    self.th.append(self.instance._meta.verbose_name)
                    continue
                field_obj = self.instance._meta.get_field(field)
                self.th.append(field_obj.verbose_name)
    
            #自定义额外字段
            for item in self.extra_field:
                for k,v in item.items():
                    if v.get("verbose_name"):
                        self.th.append(v.get("verbose_name"))
                    else:
                        self.th.append(k)
    
            for query in self.querysets:
                tds = []
                for th in self.display_list:
                    if th == "self":
                        field_val = "%s" % query
                    elif len(self.instance._meta.get_field(th).choices) > 0:
                        field_val = "%s"%getattr(query,"get_%s_display"%th)()
                    else:
                        field_val = "%s"%getattr(query,th)
                    if self.field_tag.get(th):
                        # {"self": {"a": {"href": "127.0.0.1:8000","href": "127.0.0.1:8000"},"a": {"href": "127.0.0.1:8000"}}}
                        tags = self.field_tag.get(th)
                        # {"a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}, "a": {"href": "127.0.0.1:8000"}}    #前面是内层,后面是外层
                        for k,v in tags.items():
                            # "a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}
                            new_attr = []
                            for k1,v1 in v.items(): #{"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}
                                pat = re.compile("{(.*?)}")
                                res = pat.search(v1)
                                while res:
                                    v1 = pat.sub(str(getattr(query,res.group(1))),v1,1)
                                    res = pat.search(v1)
    
                                new_attr.append("%s='%s'"%(k1,v1))  #获取到所有的属性放在列表中
                            field_val = "<%s %s>%s</%s>"%(k," ".join(new_attr),field_val,k)
                    tds.append(field_val)
    
                for item in self.extra_field:
                    for e_k,e_v in item.items():
                        if e_v.get("value"):
                            field_val = "%s" % e_v.get("value")
                        else:
                            if hasattr(e_v.get("function"),"__call__"):
                                field_val = e_v.get("function")(getattr(query,e_v.get("model_attr")),*e_v.get("args",()),**e_v.get("kwargs",{}))
                            else:
                                field_val = getattr(getattr(query,e_v.get("model_attr")),e_v.get("function"))(*e_v.get("args",()),**e_v.get("kwargs",{}))
                        if self.field_tag.get(e_k):
                            # {"self": {"a": {"href": "127.0.0.1:8000","href": "127.0.0.1:8000"},"a": {"href": "127.0.0.1:8000"}}}
                            tags = self.field_tag.get(e_k)
                            # {"a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}, "a": {"href": "127.0.0.1:8000"}}    #前面是内层,后面是外层
                            for k,v in tags.items():
                                # "a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}
                                new_attr = []
                                for k1,v1 in v.items(): #{"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}
                                    pat = re.compile("{(.*?)}")
                                    res = pat.search(v1)
                                    while res:
                                        v1 = pat.sub(str(getattr(query, res.group(1))), v1, 1)
                                        res = pat.search(v1)
                                    new_attr.append("%s='%s'"%(k1,v1))  #获取到所有的属性放在列表中
                                field_val = "<%s %s>%s</%s>"%(k," ".join(new_attr),field_val,k)
                        tds.append(field_val)
    
                self.tr.append(tds)
    
        def __str__(self):
            cls_attr = []
            if len(self.attrs):
                for item in self.attrs.items():
                    cls_attr.append("%s='%s'"%(item[0],item[1]))
    
            tb = "<table %s>"%(" ".join(cls_attr))
    
            tr = "<thead><tr>"
            for th_data in self.th:
                th = "<th>%s</th>"%th_data
                tr += th
            tr += "</tr></thead><tbody>"
    
            tb += tr
    
            for tr_data in self.tr:
                tr = "<tr>"
                for td_data in tr_data:
                    td = "<td>%s</td>"%td_data
                    tr += td
                tr += "</tr>"
                tb += tr
            tb += "</tbody></table>"
            return tb
    BaseForm实现通过表数据显示table

    (2)使用方法

    from Teacher.table_form import BaseForm
    
    class ClassListForm(BaseForm):
        display_list = ["self","branch","class_type","start_date","graduate_date"]   #self代表直接显示本条数据__str__
        field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},"study_record":{"a":{"href":"/teacher/classlist/{id}/class_list.html"}},"student":{"a":{"href":"/teacher/classlist/{id}/student_list.html"}}}   #在前面的标签会显示在内层,在显示的数据外面加上标签
        attrs = {"class":"table table-hover"}  #为table设置属性
        extra_field = [{"student":{'verbose_name':"学员数量","model_attr":"student_set","function":"count"}},{"study_record":{"verbose_name":"上课记录","value":"上课记录"}}]  
        #额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法,同时可以使用"args"传递元组,"kwargs":传递字典作为参数
    
        def __init__(self,model,querysets):
            super(ClassListForm, self).__init__(model,querysets)
    class StudentForm(BaseForm):
        display_list = ["self"]   #,"couser","semester"
        field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},}   #在前面的标签会显示在内层
        attrs = {"class":"table table-hover"}
        extra_field = [{"student_grade":{'verbose_name':"学员成绩","value":"N/A"}},{"study_status":{"verbose_name":"出勤状况","value":"N/A"}}]
        #额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法
    
        def __init__(self,model,querysets):
            super(StudentForm, self).__init__(model,querysets)
    StudentForm
    class CourseForm(BaseForm):
        display_list = ["self","title","content","has_homework","homework","date"]   #,"couser","semester"
        field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},}   #在前面的标签会显示在内层
        attrs = {"class":"table table-hover"}
        extra_field = []
        #额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法
    
        def __init__(self,model,querysets):
            super(CourseForm, self).__init__(model,querysets)
    CourseForm

    (3)在views中调用各个tableform

    @login_required
    def course_list(request):
        course_querysets = models.ClassList.objects.filter(
            teachers=request.user.userprofile
        )
    
        #自定义form,用于table
        forms = ClassListForm(models.ClassList,course_querysets)
        forms.register()
    
        return render(request,"teacher/class_list.html",locals())
    
    
    @login_required
    def student_list(request,c_id):
        student_querysets = models.ClassList.objects.filter(
            teachers = request.user.userprofile,
            id = c_id
        ).get().student_set.all()
    
        forms = StudentForm(models.Student,student_querysets)
        forms.register()
    
        return render(request,"teacher/student_list.html",locals())
    
    @login_required
    def classRec_list(request,c_id):
        course_querysets = models.ClassList.objects.filter(
            teachers = request.user.userprofile,
            id = c_id
        ).get().courserecord_set.all()
    
        forms = CourseForm(models.CourseRecord,course_querysets)
        forms.register()
    
        return render(request, "teacher/course_list.html", locals())
    所有显示table的函数

    (4)前端调用{{ forms|safe }},form是定义的tableform变量,实现简单显示页面

    {% extends "index.html" %}
    {% load my_func %}
    
    {% block right-content-container %}
    
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">课程列表</h3>
        </div>
        <div class="panel-body">
            {{ forms|safe }}
        </div>
    </div>
    {% endblock %}
    class_list.html
    {% extends "index.html" %}
    {% load my_func %}
    
    {% block right-content-container %}
    
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">课程记录</h3>
        </div>
        <div class="panel-body">
            {{ forms|safe }}
            <a href="{% url 'classRec_add' c_id %}" class="btn btn-primary">添加记录</a>
        </div>
    </div>
    {% endblock %}
    course_list.html
    {% extends "index.html" %}
    {% load my_func %}
    
    {% block right-content-container %}
    
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">学员列表</h3>
        </div>
        <div class="panel-body">
            {{ forms|safe }}
        </div>
    </div>
    {% endblock %}
    student_list.html

    (5)添加记录

    from django.forms import ModelForm,forms
    from repository import models
    
    class CustomerForm(ModelForm):
        class Meta:
            model = models.CustumerInfo #将表与元类中的数据关联
            fields = "__all__"
            exclude = ["consult_content","status","consult_courses"]
            readonly_fields = ['contact_type',"contact","consultant",'referral_from','source']
    
        def __new__(cls, *args, **kwargs):
            #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
            #这张表中的所有字段对象
            for field_name,field_obj in dict(cls.base_fields).items():
                field_obj.widget.attrs.update({'class':"form-control"})
    
                if field_name in cls.Meta.readonly_fields:
                    field_obj.widget.attrs.update({'disabled': "true"})
    
            return ModelForm.__new__(cls)
    
        def clean(self):
            if self.errors:
                raise forms.ValidationError("Please fix errors before re-submit")
    
            if self.instance.id is not None:    #这是一个修改的表单,而不是添加
                for field_name in self.Meta.readonly_fields:
                    new_val = self.cleaned_data[field_name]
                    old_val = getattr(self.instance, field_name)
                    if new_val != old_val:
                        self.add_error(field_name, "ReadOnly fileds error: you cannot change %s"%old_val)
    
    class CourseRecordForm(ModelForm):
        class Meta:
            model = models.CourseRecord #将表与元类中的数据关联
            fields = "__all__"
            # exclude = ["teacher"]
    
        def __new__(cls, *args, **kwargs):
            #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
            #这张表中的所有字段对象
            for field_name,field_obj in dict(cls.base_fields).items():
                field_obj.widget.attrs.update({'class':"form-control"})
    
            return ModelForm.__new__(cls)
    使用form类,生成控件
    @login_required
    def classRec_add(request,c_id):
        if request.method == "GET":
            CRfm = forms.CourseRecordForm()
        else:
            CRfm = forms.CourseRecordForm(data=request.POST)
            CRfm.save()
            return redirect("/teacher/classlist/%s/course_list.html"%c_id)
    
        return render(request,"teacher/course_add.html",locals())
    views调用classRec_add,进行显示和添加数据
    {% extends "index.html" %}
    {% load my_func %}
    
    {% block right-content-container %}
    
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">课程记录添加</h3>
        </div>
        <div class="panel-body">
            <form action="" method="post" class="form-group form-horizontal">
            {% csrf_token %}
                {% for field in CRfm %}
                <div class="col-sm-6">
                    <div class="col-lg-4">
                        <label for="" class="control-label">{{ field.label }}</label>
                    </div>
                    <div class="col-lg-8">
                        {{ field }}
                    </div>
                </div>
                {% endfor %}
                <div>
                    <input type="submit" class="btn btn-primary" value="添加">
                </div>
            </form>
        </div>
    </div>
    {% endblock %}
    前端显示course_add.html

    二:学员功能,实现课程显示,作业提交

    (一)根据邮件中的账号密码登录

    (二)实现查看班级,查看课程记录,提交作业

     

    (1)由于这里也是table多使用,可以继续使用tableform

    from Teacher.table_form import BaseForm
    
    class ClassListForm(BaseForm):
        display_list = ["self","class_type","start_date","graduate_date",]   #,"couser","semester"
        field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'score':{"a":{"href":"127.0.0.1:8080"},},'mng_homework':{"a":{"href":"/student/course.html"}}}   #在前面的标签会显示在内层
        attrs = {"class":"table table-hover"}
        extra_field = [{"score":{"verbose_name":"成绩","value":"成绩排名"}},{"mng_homework":{'verbose_name':"作业管理","value":"作业管理"}},]
    
    
    
    class ClassRecordForm(BaseForm):
        display_list = ["self","title","teacher","content","has_homework","homework"]   #,"couser","semester"
        field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'fin_homework':{"a":{"href":"/student/homework/{id}.html"}}}   #在前面的标签会显示在内层
        attrs = {"class":"table table-hover"}
        extra_field = [{"date":{"verbose_name":"日期","model_attr":"date","function":"strftime","args":("%Y-%m-%d %H:%M:%S",)}},{"fin_homework":{'verbose_name':"我的作业","value":"提交作业"}},]
        #额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法
    
        def __init__(self,model,querysets):
            super(ClassRecordForm, self).__init__(model,querysets)

    (2)view中调用,显示班级和课程,前端也是{{forms|safe}}

    # Create your views here.
    @login_required
    def couse_list(request):
        course_list = models.CourseRecord.objects.filter(
            class_grade__student=request.user.userprofile.student
        ).all()
    
        forms = ClassRecordForm(models.CourseRecord,course_list)
        forms.register()
    
        return render(request,"student/class_list.html",locals())
    
    
    @login_required
    def AllClass(request):
        class_list = models.ClassList.objects.filter(
            student = request.user.userprofile.student
        ).all()
    
        forms = ClassListForm(models.ClassList,class_list)
        forms.register()
    
        return render(request,"student/class.html",locals())
    显示班级和课程

    (3)使用forms表单显示数据,使用Dropzone添加作业

    from django.forms import ModelForm,forms
    from repository import models
    
    class CourseRecordForm(ModelForm):
        class Meta:
            model = models.CourseRecord #将表与元类中的数据关联
            fields = "__all__"
            exclude = ["has_homework"]
    
        def __new__(cls, *args, **kwargs):
            #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
            #这张表中的所有字段对象
            for field_name,field_obj in dict(cls.base_fields).items():
                field_obj.widget.attrs.update({'class':"form-control","disabled":"true"})
    
            return ModelForm.__new__(cls)
    CourseRecordForm
    @login_required
    def homework(request,id):
        CRModel = models.CourseRecord.objects.filter(id=id,has_homework=True)
    
        if not CRModel.exists():
            return HttpResponse("无作业")
    
        CRInfo = CRModel.get()
        cus_dir = os.path.join(conf.settings.STUDENT_HOMEWORK_DIR, str(id), str(request.user.userprofile.student.id))
        if not os.path.isdir(cus_dir):
            os.makedirs(cus_dir)
    
        file_info = []
        file_names = os.listdir(cus_dir)
        for filename in file_names:
            file_info.append(os.stat(os.path.join(cus_dir,filename)))
    
        if request.method == "GET":
            form = myforms.CourseRecordForm(instance=CRInfo)
        else:
            status = {
                'status': True,
                "message": None
            }
            print(request.FILES)  # 需要去接收文件,前端状态才会是true
            if len(os.listdir(cus_dir)) >= 2:
                status['status'] = False
                status['message'] = "文件超出上传个数"
                return HttpResponse(json.dumps(status))
    
            file_obj = request.FILES.get("file")
    
            with open(os.path.join(cus_dir, file_obj.name), "wb") as fp:
                for chunks in file_obj.chunks():
                    fp.write(chunks)
    
            return HttpResponse(json.dumps(status))
    
        return render(request,"student/homework.html",locals())
    homework作业显示和添加

    (4)前端代码,使用Dropzone处理数据,以及ajax删除数据

    {% extends "index.html" %}
    {% load my_func %}
    
    {% block extra-link %}
    <link rel="stylesheet" href="/static/plugins/dropzone/dropzone.css">
    {% endblock %}
    
    
    {% block right-content-container %}
    
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">作业提交</h3>
        </div>
        <div class="panel-body">
            {{ form }}
            <div class="col-sm-12">
            <table class="table table-hover" id="file_table">
              <caption><label class="control-label" style="padding-top: 0px;">已上传的文件目录</label></caption>
              <thead>
                <tr>
                  <th>文件名</th>
                  <th>大小(KB)</th>
                  <th>上传时间</th>
                  <th>删除</th>
                </tr>
              </thead>
              <tbody>
                {% for file in file_info %}
                    <tr>
                        <td>{% get_list_value file_names forloop.counter0  %}</td>
                        <td>{{ file.st_size }}</td>
                        <td>{% get_date_str file.st_atime %}</td>
                        <td><span style="color: red;" class="glyphicon glyphicon-remove" onclick="deleteFile(this);"></span></td>
                    </tr>
                {% endfor %}
              </tbody>
            </table>
            <form action="{% url 'homework' CRInfo.id %}" id="myAwesomeDropzone" class="dropzone">
                {% csrf_token %}
              <div class="fallback">
                <input name="file" type="file" multiple />
              </div>
            </form>
      </div>
        </div>
    </div>
    {% endblock %}
    
    {% block extra-js %}
    <script src="/static/plugins/dropzone/dropzone.js"></script>
    <script>
    $(function () {
        Dropzone.options.myAwesomeDropzone = {
          paramName: "file", // The name that will be used to transfer the file
          maxFilesize: 2, // MB
          maxFiles:2,
          parallelChunkUploads:true,
          accept: function(file, done) {
            if (file.name == "justinbieber.jpg") {
              done("Naha, you don't.");
            }
            else { done(); }
          },
          init: function() {
            this.on("success", function(file,respone) {
                /* Maybe display some more file information on your page */
                var rep = JSON.parse(respone)
                if(!rep.status){
                    alert(rep.message);
                    return;
                }else{
                    var myDate = new Date();
                    var str_tm = myDate.toLocaleString();
                    str_tm = str_tm.replace(///g, "-");
                    str_tm = str_tm.replace(/[u4e00-u9fa5]+/g, "");
    
                    var tr = "<tr><td>"+file.name+"</td><td>"+file.size+"</td><td>"+str_tm+"</td><td>"+'<span style="color: red;"  onclick="deleteFile(this);" class="glyphicon glyphicon-remove"></span></td></tr>'
                    $("#file_table").append(tr);
                }
            });
          }
        };
    });
    
    
    function deleteFile(ths){
        var filename = $($(ths).parents("tr").children()[0]).text()
        $.ajax({
            url:"/student/delete_file.html",
            data:{'c':'{{ id }}','f':filename,'csrfmiddlewaretoken':'{{ csrf_token }}'},
            dataType:"json",
            type:"post",
            success:function(data){
                if(data.status){
                    $(ths).parents("tr").remove()
                }else{
                    alert(data.message)
                }
            }
        })
    }
    
    </script>
    
    {% endblock %}
    homework.html

    (5)后台处理数据删除

    @login_required
    def delete_files(request):
        if request.method == "POST":
            status = {
                'status':True,
                'message':""
            }
            c_id = request.POST['c']
            filename = request.POST['f']
    
            current_path = os.path.join(conf.settings.STUDENT_HOMEWORK_DIR, str(c_id), str(request.user.userprofile.student.id))
            file_path = os.path.join(current_path,filename)
            print(file_path)
            if not os.path.isfile(file_path):
                status['status']=False
                status['message'] = "没有权限"
                return HttpResponse(json.dumps(status))
            else:
                os.remove(file_path)
    
            return HttpResponse(json.dumps(status))
    delete_files根据ajax上传数据删除文件

     总结:学会偷懒,化繁为简,学会总结业务,再去动态处理,而不是一直对数据库的增删改查,和重复一个业务逻辑

  • 相关阅读:
    ZOJ 1217 eight
    COJ 1080 A simple maze
    八数码(双向广搜)
    HDOJ 1043 eight
    [HDOJ] 小兔的棋盘
    ZOJ 2110 Tempter of the Bone
    POJ 2406 Power Strings
    [HDOJ] goagain的超级数列
    COJ 1216 异或最大值
    八数码(IDA*)
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9084942.html
Copyright © 2020-2023  润新知