CRM相关代码

0|1CRM目录结构


from django.shortcuts import  HttpResponse,render,redirect
from django.conf.urls import url
from django.utils.safestring import mark_safe
from django.urls import reverse
from django.forms import ModelForm
from stark.utils.my_page import Pagination
from django.db.models import Q

class ShowList(object):
    def __init__(self,config_obj,queryset,request):
        self.config_obj = config_obj
        self.queryset = queryset
        self.request = request
        self.page_obj = Pagination(self.request.GET.get('page',1),self.queryset.count(),self.request.GET)
        self.page_queryset = self.queryset[self.page_obj.start:self.page_obj.end]


    def get_header(self):
        # 表头数据 [str1,str2,]
        head_list = []
        for head_field in self.config_obj.get_new_list_display():
            if isinstance(head_field, str):
                if head_field == '__str__':
                    val = self.config_obj.model._meta.model_name.upper()
                else:
                    val = self.config_obj.model._meta.get_field(head_field).verbose_name
            else:
                val = head_field(self.config_obj, is_header=True)
            head_list.append(val)
        return head_list

    def get_body(self):
        # 表单数据 [[attr1,attr2,],[attr1,attr2,],[]]
        body_list = []
        for data in self.page_queryset:
            tmp = []
            for field in self.config_obj.get_new_list_display():
                if isinstance(field, str):
                    val = getattr(data, field)
                    if field != '__str__':
                        field_obj = self.config_obj.model._meta.get_field(field)
                        if field_obj.choices:
                            val = getattr(data,'get_'+field+'_display')
                    if field in self.config_obj.list_display_links:
                        _url = self.config_obj.get_reverse_url('edit', data)
                        val = mark_safe('<a href="%s">%s</a>' % (_url, val))
                else:
                    val = field(self.config_obj, obj=data)
                tmp.append(val)
            body_list.append(tmp)
        return body_list

    def get_action(self):
        # action标签渲染
        tmp = []
        for action in self.config_obj.actions:
            tmp.append({
                'name': action.__name__,
                'desc': action.desc
            })
        return tmp

    def get_filter(self):
        tmp_dict = {}
        for field in self.config_obj.list_filter:
            rel_model = self.config_obj.model._meta.get_field(field).rel.to
            rel_queryset = rel_model.objects.all()
            click_id = self.request.GET.get(field)
            tmp = []

            import copy
            params1 = copy.deepcopy(self.request.GET)
            if field in params1:
                params1.pop(field)
                s = mark_safe("<a href='?%s'>All</a>"%params1.urlencode())
            else:
                s = mark_safe("<a href=''>All</a>")
            tmp.append(s)

            for data in rel_queryset:
                import copy
                params = copy.deepcopy(self.request.GET)
                params[field] = data.pk
                if click_id == str(data.pk):
                    s = mark_safe('<a href="?%s" class="active">%s</a>'%(params.urlencode(),str(data)))
                else:
                    s = mark_safe('<a href="?%s">%s</a>'%(params.urlencode(),str(data)))
                tmp.append(s)
            tmp_dict[field] = tmp
        return tmp_dict

class ModelStark(object):
    list_display = ['__str__',]
    list_display_links = []
    model_form_class = None
    search_fields = []
    actions = []
    list_filter = []

    def __init__(self,model):
        self.model = model
        self.app_label = self.model._meta.app_label
        self.model_name = self.model._meta.model_name
        self.key_word = ''

    def cho_col(self,is_header=False,obj=None):
        if is_header:
            return '选择'
        return mark_safe("<input type='checkbox' value='%s' name='selected_actions'>"%obj.pk)

    def edit_col(self,is_header=False,obj=None):
        if is_header:
            return '编辑'
        _url = self.get_reverse_url('edit',obj)
        return mark_safe('<a href="%s">编辑</a>'%_url)

    def del_col(self,is_header=False,obj=None):
        if is_header:
            return '删除'
        _url = self.get_reverse_url('delete',obj)
        return mark_safe('<a href="%s">删除</a>'%_url)

    def get_reverse_url(self,type,obj=None):
        if obj:
            _url = reverse('%s_%s_%s'%(self.app_label,self.model_name,type),args=(obj.pk,))
        else:
            _url = reverse('%s_%s_%s'%(self.app_label,self.model_name,type))
        return _url

    def get_new_list_display(self):
        tmp = []
        tmp.append(ModelStark.cho_col)
        tmp.extend(self.list_display)
        if not self.list_display_links:
            tmp.append(ModelStark.edit_col)
        tmp.append(ModelStark.del_col)
        return tmp

    def get_search(self,request,queryset):
        key_word = request.GET.get('q')
        self.key_word = ''
        if key_word:
            self.key_word = key_word
            q = Q()
            q.connector = 'or'
            for search_field in self.search_fields:
                q.children.append((search_field + '__icontains', key_word))
            queryset = queryset.filter(q)
        return queryset

    def get_filter(self,request,queryset):
        q = Q()
        for field,field_id in request.GET.items():
            if field in ['page','q']:
                continue
            field_id = request.GET.get(field)
            q.children.append((field, field_id))
        queryset = queryset.filter(q)
        return queryset

    def list_view(self,request):
        # action功能
        if request.method == 'POST':
            pk_list = request.POST.getlist('selected_actions')
            action = request.POST.get('action')
            if action:
                action_func = getattr(self,action)
                queryset = self.model.objects.filter(pk__in=pk_list)
                action_func(request,queryset)

        queryset = self.model.objects.all()
        # search功能
        queryset = self.get_search(request,queryset)
        # filter功能
        queryset = self.get_filter(request,queryset)

        show_obj = ShowList(self,queryset,request)

        url = self.get_reverse_url('add')
        return render(request, 'stark/list.html', locals())

    def get_model_form_class(self):
        if self.model_form_class:
            return self.model_form_class
        class ModelFormClass(ModelForm):
            class Meta:
                model = self.model
                fields = '__all__'
        return ModelFormClass

    def add_view(self,request):
        model_form_class = self.get_model_form_class()
        if request.method == 'POST':
            model_form_obj = model_form_class(request.POST)
            pop_back_id = request.GET.get('pop_back_id')
            if model_form_obj.is_valid():
                obj = model_form_obj.save()
                print(obj)
                if pop_back_id:
                    pk = obj.pk
                    text = str(obj)
                    return render(request, 'stark/pop.html', locals())
                return redirect(self.get_reverse_url('list'))
        model_form_obj = model_form_class()
        for form_field in model_form_obj:
            from django.forms.models import ModelChoiceField
            if isinstance(form_field.field,ModelChoiceField):
                form_field.is_pop = True
                rel_model = self.model._meta.get_field(form_field.name).rel.to
                rel_app_label = rel_model._meta.app_label
                rel_model_name = rel_model._meta.model_name
                url = reverse('%s_%s_add'%(rel_app_label,rel_model_name))
                url = url + '?pop_back_id=' + form_field.auto_id
                form_field.url = url
        return render(request, 'stark/add.html', locals())

    def edit_view(self,request,id):
        model_form_class = self.get_model_form_class()
        edit_obj = self.model.objects.filter(pk=id).first()
        if request.method == 'POST':
            model_form_obj = model_form_class(request.POST,instance=edit_obj)
            if model_form_obj.is_valid():
                model_form_obj.save()
                return redirect(self.get_reverse_url('list'))
        model_form_obj = model_form_class(instance=edit_obj)
        return render(request, 'stark/edit.html', locals())

    def delete_view(self,request,id):
        delete_obj = self.model.objects.filter(pk=id).delete()
        return redirect(self.get_reverse_url('list'))

    def get_urls(self):
        tmp = [
            url(r'^$',self.list_view,name='%s_%s_list'%(self.app_label,self.model_name)),
            url(r'^add/',self.add_view,name='%s_%s_add'%(self.app_label,self.model_name)),
            url(r'edit/(d+)/',self.edit_view,name='%s_%s_edit'%(self.app_label,self.model_name)),
            url(r'delete/(d+)/',self.delete_view,name='%s_%s_delete'%(self.app_label,self.model_name))
        ]
        tmp.extend(self.extra_url())
        return tmp,None,None

    def extra_url(self):
        return []

class AdminSite(object):
    def __init__(self, name='admin'):
        self._registry = {}  # model_class class -> admin_class instance

    def register(self, model, admin_class=None, **options):
        if not admin_class:
            admin_class = ModelStark
        self._registry[model] = admin_class(model)

    def get_urls(self):
        tmp = []
        for class_name,config_obj in self._registry.items():
            app_label = class_name._meta.app_label
            model_name = class_name._meta.model_name
            tmp.append(
                url(r'^%s/%s/'%(app_label,model_name),config_obj.get_urls())
            )
        return tmp

    @property
    def urls(self):
        return self.get_urls(), None, None

site = AdminSite()
service/stark.py
class Pagination(object):
    def __init__(self, current_page, all_count,params, per_page_num=10, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数

        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        获取数据用page_data而不再使用原始的queryset
        获取前端分页样式用page_obj.page_html
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

        import copy
        self.params = copy.deepcopy(params)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        res = self.params
        for i in range(pager_start, pager_end):
            res['page'] = i
            if i == self.current_page:
                temp = '<li class="active"><a href="?%s">%s</a></li>' % (res.urlencode(), i,)
            else:
                temp = '<li><a href="?%s">%s</a></li>' % (res.urlencode(), i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)
utils/my_page

templates/stark

{% extends 'stark/base.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/css/common.css">
{% endblock %}
{% block content %}

    <h3>添加数据</h3>
    <form action="" method="post">
        {% csrf_token %}
        {% for foo in model_form_obj %}
        <div class="form-inline fa-plus">
            <p>{{ foo.label }}{{ foo }}</p>
            {% if foo.is_pop %}
            <span class="plus" onclick="openWindow('{{ foo.url }}')">+</span>
            {% endif %}
        </div>
        {% endfor %}
        <input type="submit" class="btn btn-info pull-right">
    </form>

    <script>
        function openWindow(url) {
            window.open(url,'','width=800px,height=400px');
        }

        function add_options(pop_back_id,pk,text) {
            var opEle = document.createElement('option');
            opEle.value = pk;
            opEle.innerText = text;
            opEle.selected = 'selected';
            var seEle = document.getElementById(pop_back_id);
            seEle.appendChild(opEle);
        }
    </script>

{% endblock %}
add.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <style>
        .table {
            margin-top: 10px;
        }
        .active {
            color: red;
        }
        .fa-plus {
            position: relative;
        }
        .plus {
            font-size: 24px;
            color: #336699;
            position: absolute;
            right: -31px;
            top: 19px;
        }
    </style>
    {% block css %}

    {% endblock %}
</head>
<body>
{#  导航条开始 #}
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <a class="navbar-brand" href="#">CRM</a>
    </div>
    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav navbar-right">
        <li>{% if request.session.user %}
            <a href="">{{ request.session.user }}</a>
            {% else %}
            <a href="/login/">登陆</a>
        {% endif %}</li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
          <ul class="dropdown-menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="/logout/">注销</a></li>
          </ul>
        </li>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
{#  导航条结束 #}



<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="menu">
                <p><a href="/index">首页</a></p>
                {% for foo in request.session.permission_menu_list %}
                <p><a href="{{ foo.url }}">{{ foo.title }}</a></p>
                {% endfor %}
            </div>
        </div>
        <div class="col-md-9">
            {% block content %}

            {% endblock %}
        </div>
    </div>
</div>


</body>
</html>
base.html
{% extends 'stark/base.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/css/common.css">
{% endblock %}
{% block content %}

    <h3>编辑数据</h3>
    <form action="" method="post">
        {% csrf_token %}
        {% for foo in model_form_obj %}
        <p>{{ foo.label }}{{ foo }}</p>
        {% endfor %}
        <input type="submit" class="btn btn-success pull-right">
    </form>





{% endblock %}
edit.html
{% extends 'stark/base.html' %}


{% block content %}
<div class="jumbotron">
  <h1>Hello, world!</h1>
  <p>...</p>
  <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
{% endblock %}
index
{% extends 'stark/base.html' %}

{% block css %}
    <script src="/static/js/check.js"></script>
{% endblock %}

{% block content %}
        <h3>查看数据</h3>
        <a href="{{ url }}" class="btn btn-primary">添加数据</a>
        {#    search功能开始#}
        {% if show_obj.config_obj.search_fields %}
            <form class="form-inline pull-right">
                <div class="form-group">
                    <div class="input-group">
                        <input type="text" class="form-control" id="exampleInputAmount" placeholder="关键字" name="q"
                               value="{{ show_obj.config_obj.key_word }}">
                    </div>
                </div>
                <button type="submit" class="btn btn-danger">Search</button>
            </form>
        {% endif %}
        {#    search功能结束#}
        {#    action功能开始#}
        <form action="" class="form-inline" method="post">
            {% csrf_token %}
            <select class="form-control" name="action">
                <option value="">-------------------------</option>
                {% for foo in show_obj.get_action %}
                    <option value="{{ foo.name }}">{{ foo.desc }}</option>
                {% endfor %}
            </select>
            <input type="submit" value="Go!" class="btn btn-info">
            <table class="table table-bordered table-striped table-hover">
                <thead>
                <tr>
                    {% for head in show_obj.get_header %}
                        <th>{{ head }}</th>
                    {% endfor %}
                </tr>
                </thead>
                <tbody>
                {% for data in show_obj.get_body %}
                    <tr>
                        {% for foo in data %}
                            <td>{{ foo }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
        {{ show_obj.page_obj.page_html|safe }}

    {% if show_obj.config_obj.list_filter %}
        <div class="col-md-3">
            <div class="alert-info text-center">FILTER</div>
            {% for foo,data_list in show_obj.get_filter.items %}
                <div class="panel panel-default">
                    <div class="panel-heading">By {{ foo }}</div>
                    <div class="panel-body">
                        {% for data in data_list %}
                            <p>{{ data }}</p>
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
        </div>
    {% endif %}

{% endblock %}
list.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
    window.opener.add_options('{{ pop_back_id }}','{{ pk }}','{{ text }}');
    window.close();

</script>

</body>
</html>
pop.html

permissionmiddle

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect,HttpResponse
import re

class PermissionMiddleWare(MiddlewareMixin):
    def process_request(self,request):
        # 当前用户访问路径
        current_path = request.path
        # 用户访问白名单
        white_list = ['/login/','/admin/*']
        for white_permission in white_list:
            res = re.search(white_permission,current_path)
            if res:
                return None
        # 校验是否登陆
        if not request.session.get('user'):
            return redirect('/login')
        # 获取当前用户访问权限
        permission_list = request.session.get('permission_list')

        # 判断是否有权访问

        for permission_rex in permission_list:
            permission_rex = '^%s$'%permission_rex
            res = re.search(permission_rex,current_path)
            if res:
                return None
        return HttpResponse('没有访问权限')
permission.py

static/js

window.onload = function () {
 $('.xxx').change(function () {
     var status = $(this).val();
     var pk = $(this).attr('pk');
     $.ajax({
         url:'/stark/app01/studentstudyrecord/check_work/',
         type:'post',
         data:{
             status:status,
             pk:pk
         },
         success:function (data) {
             console.log(data)
         }
     })
 })
};
check.js

app01

from django.shortcuts import render,redirect
from app01 import models
# Create your views here.
def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj = models.User.objects.filter(name=username,pwd=password).first()
        if user_obj:
            # 记录登陆状态
            request.session['user'] = username
            # 记录权限列表
            permission_list = []
            permission_menu_list = []
            res = user_obj.roles.values('permissions__url','permissions__title','permissions__code').distinct()
            for permission in res:
                permission_list.append(permission.get('permissions__url'))
                if permission.get('permissions__code') == 'list':
                    permission_menu_list.append({
                        'url':permission.get('permissions__url'),
                        'title':permission.get('permissions__title'),
                    })
            request.session['permission_menu_list'] = permission_menu_list
            request.session['permission_list'] = permission_list
            return redirect('/index')
    return render(request,'login.html')

def logout(request):
    request.session.clear()
    return redirect('/index')

def index(request):
    return render(request,'stark/index.html')
views.py
from app01 import models
from stark.service.stark import site,ModelStark
from django.utils.safestring import mark_safe
from django.conf.urls import url
from django.shortcuts import render,redirect,HttpResponse

class UserConfig(ModelStark):
    list_display = ['name','pwd','roles']

site.register(models.User,UserConfig)
site.register(models.Role)
class PermissionConfig(ModelStark):
    list_display = ['title','url']
    list_display_links = ['title']
site.register(models.Permission,PermissionConfig)

# CRM相关表
site.register(models.School)
site.register(models.Course)
site.register(models.Department)
site.register(models.UserInfo)
site.register(models.ClassList)

class CustomerConfig(ModelStark):
    def show_consult_course(self,is_header=False,obj=None):
        if is_header:
            return '咨询课程'
        course_list = []
        for course in obj.course.all():
            course_list.append('<a href="/stark/app01/customer/cancel_course/%s/%s/" style="border: 1px solid blue;padding: 3px 6px">%s</a>'%(obj.pk,course.pk,course.name))
        return mark_safe(' '.join(course_list))

    def cancel_course(self, request, customer_id, course_id):
        customer_obj = models.Customer.objects.filter(pk=customer_id).first()
        customer_obj.course.remove(course_id)
        return redirect(self.get_reverse_url('list'))

    def extra_url(self):
        tmp = []
        tmp.append(
            url(r'^cancel_course/(d+)/(d+)/', self.cancel_course)
        )
        return tmp
    list_display = ['name','gender',show_consult_course]


site.register(models.Customer,CustomerConfig)

site.register(models.ConsultRecord)
site.register(models.Student)


class ClassStudyRecordConfig(ModelStark):
    def patch_init(self,request,queryset):
        # queryset是选中的某个班级当天的课程记录
        tmp = []
        for course_record_obj in queryset:
            student_list = models.Student.objects.filter(class_list=course_record_obj.class_obj.pk)
            for student_obj in student_list:
                obj = models.StudentStudyRecord(classstudyrecord=course_record_obj,student=student_obj)
                tmp.append(obj)
        models.StudentStudyRecord.objects.bulk_create(tmp)
    patch_init.desc = '批量生成学生学习记录'
    actions = [patch_init]

    def record(self,is_header=False,obj=None):
        if is_header:
            return '记录'
        _url = '/stark/app01/studentstudyrecord/?classstudyrecord=%s'%obj.pk
        return mark_safe("<a href='%s'>记录</a>"%_url)

    list_display = ['class_obj','day_num',record]
site.register(models.ClassStudyRecord,ClassStudyRecordConfig)
class StudentStudyRecordConfig(ModelStark):
    def patch_late(self,request,queryset):
        queryset.update(record='late')
    patch_late.desc = '批量处理迟到'
    actions = [patch_late]

    def work(self,is_header=False,obj=None):
        if is_header:
            return '考勤'
        html = ''
        for k in obj.record_choices:
            if obj.record == k[0]:
                s = "<option  value='%s' selected>%s</option>"%k
            else:
                s = "<option  value='%s' >%s</option>"%k
            html += s
        return mark_safe("<select class='xxx' pk=%s>%s</select>"%(obj.pk,html))

    def check(self,request):
        status = request.POST.get('status')
        pk = request.POST.get('pk')
        print(status,pk)
        models.StudentStudyRecord.objects.filter(pk=pk).update(record=status)
        return HttpResponse('ok')

    def extra_url(self):
        tmp = []
        tmp.append(
            url(r'^check_work/',self.check)
        )
        return tmp

    list_display = ['classstudyrecord', 'student', 'record', 'score',work]
site.register(models.StudentStudyRecord,StudentStudyRecordConfig)
stark.py
from django.db import models

# Create your models here.
# rbac权限相关表格
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    roles = models.ManyToManyField(to='Role')

    def __str__(self):
        return self.name

class Role(models.Model):
    title = models.CharField(max_length=32)
    permissions = models.ManyToManyField(to='Permission')

    def __str__(self):
        return self.title

class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=32)
    code = models.CharField(max_length=32,default='list')

    def __str__(self):
        return self.title


# CRM相关内部表
class School(models.Model):
    """
    校区表
    如:
        北京沙河校区
        上海校区

    """
    title = models.CharField(verbose_name='校区名称', max_length=32)

    def __str__(self):
        return self.title

class Course(models.Model):
    """
    课程表
    如:
        Linux基础
        Linux架构师
        Python自动化开发精英班
        Python自动化开发架构师班
        Python基础班
        go基础班
    """
    name = models.CharField(verbose_name='课程名称', max_length=32)

    def __str__(self):
        return self.name

class Department(models.Model):
    """
    部门表
    市场部     1000
    销售       1001

    """
    title = models.CharField(verbose_name='部门名称', max_length=16)
    code = models.IntegerField(verbose_name='部门编号', unique=True, null=False)

    def __str__(self):
        return self.title

class UserInfo(models.Model):
    """
    员工表
    """

    name = models.CharField(verbose_name='员工姓名', max_length=16)
    email = models.EmailField(verbose_name='邮箱', max_length=64)
    depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code")
    user=models.OneToOneField("User",default=1)
    def __str__(self):
        return self.name

class ClassList(models.Model):
    """
    班级表
    如:
        Python全栈  面授班  5期  10000  2017-11-11  2018-5-11
    """
    school = models.ForeignKey(verbose_name='校区', to='School')
    course = models.ForeignKey(verbose_name='课程名称', to='Course')
    semester = models.IntegerField(verbose_name="班级(期)")


    price = models.IntegerField(verbose_name="学费")
    start_date = models.DateField(verbose_name="开班日期")
    graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
    memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )

    teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',limit_choices_to={'depart':1002})
    tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo',related_name="class_list",limit_choices_to={'depart':1006})


    def __str__(self):
        return "{0}({1}期)".format(self.course.name, self.semester)


class Customer(models.Model):
    """
    客户表
    """
    qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一')

    name = models.CharField(verbose_name='学生姓名', max_length=16)
    gender_choices = ((1, ''), (2, ''))
    gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)

    education_choices = (
        (1, '重点大学'),
        (2, '普通本科'),
        (3, '独立院校'),
        (4, '民办本科'),
        (5, '大专'),
        (6, '民办专科'),
        (7, '高中'),
        (8, '其他')
    )
    education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
    graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
    major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True)

    experience_choices = [
        (1, '在校生'),
        (2, '应届毕业'),
        (3, '半年以内'),
        (4, '半年至一年'),
        (5, '一年至三年'),
        (6, '三年至五年'),
        (7, '五年以上'),
    ]
    experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
    work_status_choices = [
        (1, '在职'),
        (2, '无业')
    ]
    work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
                                      null=True)
    company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
    salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True)

    source_choices = [
        (1, "qq群"),
        (2, "内部转介绍"),
        (3, "官方网站"),
        (4, "百度推广"),
        (5, "360推广"),
        (6, "搜狗推广"),
        (7, "腾讯课堂"),
        (8, "广点通"),
        (9, "高校宣讲"),
        (10, "渠道代理"),
        (11, "51cto"),
        (12, "智汇推"),
        (13, "网盟"),
        (14, "DSP"),
        (15, "SEO"),
        (16, "其它"),
    ]
    source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
    referral_from = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        verbose_name="转介绍自学员",
        help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
        related_name="internal_referral"
    )
    course = models.ManyToManyField(verbose_name="咨询课程", to="Course")

    status_choices = [
        (1, "已报名"),
        (2, "未报名")
    ]
    status = models.IntegerField(
        verbose_name="状态",
        choices=status_choices,
        default=2,
        help_text=u"选择客户此时的状态"
    )

    consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter',limit_choices_to={'depart':1001})

    date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
    recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True)
    last_consult_date = models.DateField(verbose_name="最后跟进日期", )

    def __str__(self):
        return self.name

class ConsultRecord(models.Model):
    """
    客户跟进记录
    """
    customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer')
    consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo',limit_choices_to={'depart':1001})
    date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
    note = models.TextField(verbose_name="跟进内容...")

    def __str__(self):
        return self.customer.name + ":" + self.consultant.name

class Student(models.Model):
    """
    学生表(已报名)
    """
    customer = models.OneToOneField(verbose_name='客户信息', to='Customer')
    class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True)

    emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
    company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
    location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
    position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
    salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
    welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
    date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
    memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)

    def __str__(self):
        return self.customer.name

class ClassStudyRecord(models.Model):
    """
    上课记录表 (班级记录)
    """
    class_obj = models.ForeignKey(verbose_name="班级", to="ClassList")
    day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")
    teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo',limit_choices_to={'depart':1002})
    date = models.DateField(verbose_name="上课日期", auto_now_add=True)

    course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
    course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
    has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
    homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
    homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
    exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)

    def __str__(self):
        return "{0} day{1}".format(self.class_obj, self.day_num)

class StudentStudyRecord(models.Model):
    '''
    学生学习记录
    '''
    classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord")
    student = models.ForeignKey(verbose_name="学员", to='Student')







    record_choices = (('checked', "已签到"),
                      ('vacate', "请假"),
                      ('late', "迟到"),
                      ('noshow', "缺勤"),
                      ('leave_early', "早退"),
                      )
    record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
    score_choices = ((100, 'A+'),
                     (90, 'A'),
                     (85, 'B+'),
                     (80, 'B'),
                     (70, 'B-'),
                     (60, 'C+'),
                     (50, 'C'),
                     (40, 'C-'),
                     (0, ' D'),
                     (-1, 'N/A'),
                     (-100, 'COPY'),
                     (-1000, 'FAIL'),
                     )
    score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
    homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
    note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)

    homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
    stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
    date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)

    def __str__(self):
        return "{0}-{1}".format(self.classstudyrecord, self.student)
models.py

__EOF__

原文地址:https://www.cnblogs.com/longfeiwang/p/10972478.html