基於bootstrap樣式的 AdminLTE-master模板管理系統
AdminLTE-master開源模板: https://github.com/Tiger0409/AdminLTE-master
后台管理分兩個應用實現:
- 應用1:rbac 權限管理
- 應用2:web 信息展示
項目結構圖例:
一、系統配置
1.1 settings設置:
settings.py # 應用 INSTALLED_APPS = [ ... # 應用1 'rbac.apps.RbacConfig', # 應用2 'web', ] # 中間件 MIDDLEWARE = [ ... # 自定義權限中間件 'rbac.middlewares.PermissionMeddleWare', ] # 引用Django自帶的認證系統 AUTH_USER_MODEL = "rbac.UserInfo" # 設置認證失敗后跳轉的頁面,默認跳轉account/login LOGIN_URL = '/login/' # 設置靜態文件路徑 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), ] # ###### 權限相關的配置 ###### # 權限列表,通過中間件過濾 PERMISSION_SESSION_KEY = 'permissions' # 左側菜單列表,通過中間件過濾 MENU_SESSION_KEY = 'menus' # crm配置白名單 WHITE_URL_LIST = [ r"^/login/$", r"^/logout/$", # r"^/home/$", r"^/get_valid_img/$", r"^/register/$", r"^/admin/.*", r"^/favicon.ico", # 角色相關 # r"^/rbac/role/list/$", # r"^/rbac/role/edit/(\d+)$", # r"^/rbac/role/del/(\d+)$", # r"^/rbac/role/add/$", # 權限列表 # r"^/rbac/menu/list/$", # r"^/rbac/menu/add/$", # r"^/rbac/menu/edit/(\d+)$", # 權限列表操作 # r"^/rbac/permission/add/$", # r"^/rbac/permission/edit/(\d+)$", # r"^/rbac/permission/del/(\d+)$", # r"^/rbac/multi/permissions/$", # 權限分配 # r"^/rbac/distribute/permissions2/$", r"^/rbac/permissions_tree/$", ]
1.2 路由分發:
項目的urls.py配置 urlpatterns = [ url(r'^admin/', admin.site.urls), # 權限url url(r'^rbac/', include("rbac.urls")), # web應用url url(r'^', include("web.urls")), ]
二、rbac 權限相關設計
2.1 rbac應用圖例:
2.2 server/init_permission.py登錄初始化,權限數據結構設計:
from django.conf import settings from rbac import models def init_permission(request, user): # 1. 查當前登錄用戶擁有的權限 permission_query = models.Permission.objects.filter( role__userinfo__username=user.username ).values( "pk", "title", "url", "menu_id", "pid", "menu__icon", "menu__title", "menu__masterurl" ).distinct() # 存放權限信息 permission_list = [] # 存放菜單信息 menu_dict = {} for item in permission_query: # 權限列表 permission_list.append({ "url": item.get("url", None), "pk": item["pk"], "title": item["title"], "pid": item["pid"], }) # 菜單字典數據類型 if item["menu_id"]: if item["menu_id"] in menu_dict: menu_dict[item["menu_id"]]["children"].append({ "menu_id": item.get("menu_id", None), "title": item.get("title"), # 面包屑 "url": item.get("url"), "pk": item["pk"] }) else: menu_dict[item["menu_id"]] = { "title": item["menu__title"], "icon": item["menu__icon"], "masterurl": item.get("menu__masterurl", None), "children": [{ "menu_id": item.get("menu_id", None), "title": item.get("title", None), "url": item.get("url", None), "pk": item["pk"] }] } # print(permission_list) # print(menu_dict, "111") # 2. 將權限信息寫入到session request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 將菜單信息寫入到session request.session[settings.MENU_SESSION_KEY] = menu_dict
2.2 templates/rbac下的html模板文件配置:

{% extends 'base.html' %} {% load rbac %} {% block css %} <style> .user-area ul { padding-left: 20px; } .user-area li { cursor: pointer; padding: 2px 0; } .user-area li a { display: block; } .user-area li.active { font-weight: bold; color: red; } .user-area li.active a { color: red; } .role-area tr td a { display: block; } .role-area tr.active { background-color: #f1f7fd; border-left: 3px solid #fdc00f; } .permission-area tr.root { background-color: #f1f7fd; cursor: pointer; } .permission-area tr.root td i { margin: 3px; } .permission-area .node { } .permission-area .node input[type='checkbox'] { margin: 0 5px; } .permission-area .node .parent { padding: 5px 0; } .permission-area .node label { font-weight: normal; margin-bottom: 0; font-size: 12px; } .permission-area .node .children { padding: 0 0 0 20px; } .permission-area .node .children .child { display: inline-block; margin: 2px 5px; } table { font-size: 12px; } .panel-body { font-size: 12px; } .panel-body .form-control { font-size: 12px; } </style> {% endblock %} {% block content %} <div class="luffy-container"> <div class="col-md-3 user-area"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-address-book-o" aria-hidden="true"></i> 用戶信息 </div> <div class="panel-body"> <ul> {% for user in user_list %} <li class= {% if user.id|safe == uid %} "active" {% endif %}> <a href="?uid={{ user.id }}">{{ user.username }}</a></li> {% endfor %} </ul> </div> </div> </div> <div class="col-md-3 role-area"> <form method="post"> {% csrf_token %} <input type="hidden" name="postType" value="role"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-book" aria-hidden="true"></i> 角色 {% if uid %} <button type="submit" class="right btn btn-success btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 保存 </button> {% endif %} </div> <div class="panel-body" style="color: #d4d4d4;padding:10px 5px;"> 提示:點擊用戶后才能為其分配角色 </div> <table class="table"> <thead> <tr> <th>角色</th> <th>選擇</th> </tr> </thead> <tbody> {% for role in role_list %} <tr {% if role.id|safe == rid %} class="active" {% endif %}> <td><a href="?{% gen_role_url request role.id %}">{{ role.name }}</a></td> <td> {% if role.id in role_id_list %} <input type="checkbox" name="roles" value="{{ role.id }}" checked/> {% else %} <input type="checkbox" name="roles" value="{{ role.id }}"/> {% endif %} </td> </tr> {% endfor %} </tbody> </table> </div> </form> </div> <div class="col-md-6 permission-area"> <form method="post"> {% csrf_token %} <input type="hidden" name="postType" value="permission"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-sitemap" aria-hidden="true"></i> 權限分配 {% if rid %} <button class="right btn btn-success btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 保存 </button> {% endif %} </div> <div class="panel-body" style="color: #d4d4d4;padding: 10px 5px;"> 提示:點擊角色后,才能為其分配權限。 </div> <table class="table" id="body"> <tbody> </tbody> </table> </div> </form> </div> </div> {% endblock %} {% block js %} <script> $(function () { bindRootPermissionClick(); }); function bindRootPermissionClick() { $('.permission-area').on('click', '.root', function () { var caret = $(this).find('i'); if (caret.hasClass('fa-caret-right')) { caret.removeClass('fa-caret-right').addClass('fa-caret-down'); $(this).next().removeClass('hide'); } else { caret.removeClass('fa-caret-down').addClass('fa-caret-right'); $(this).next().addClass('hide'); } }) } $.ajax({ url: "/rbac/permissions_tree/", type: "get", success: function (res) { //console.log(res); $.each(res, function (i, permission) { //console.log(i, permission); var menu_title = permission["menu__title"]; var menu_pk = permission["menu__pk"]; var url = permission["url"]; var pid_id = permission["pid_id"]; var pk = permission["pk"]; var title = permission["title"]; //console.log(pid_id); if (menu_title) { if ($("#menu_" + menu_pk).length) { var s = ` <tr class='node' id="per_${pk}"> <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> <tr>`; $("#menu_" + menu_pk).parent().append(s) } else { var s = `<tr class='root' id='menu_${menu_pk}'><td>${menu_title}</td></tr> <tr class='node' id="per_${pk}"> <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> <tr>`; $("#body").append(s); } } else { var s = ` <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> `; $("#per_" + pid_id).append(s) } }); var per_id_list = {{ per_id_list }} //console.log(per_id_list); $.each(per_id_list, function (i, j) { console.log($("[value='" + j + "']")[0]); $("#body [value='" + j + "']").prop("checked", true) }) } }); </script> {% endblock %}

{% extends 'base.html' %} {% block css %} <style> ul { list-style-type: none; padding: 0; } ul li { float: left; padding: 10px; padding-left: 0; width: 80px; } ul li i { font-size: 18px; margin-left: 5px; color: #6d6565; } </style> {% endblock %} {% block content %} <form class="form-horizontal" novalidate method="post" action="" style="margin-top: 50px;width: 95%"> {% csrf_token %} {% for field in form_obj %} <div class="form-group {% if field.errors %}has-error{% endif %} "> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-6"> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <div class="form-group"> <div class="col-sm-offset-2 col-sm-6"> <button type="submit" class="btn btn-default">提交</button> </div> </div> </form> {% endblock %}

{% for menu in menu_dict.values %} {% if menu.masterurl %} <li> <a href="{{ menu.masterurl }}"> <i class="fa {{ menu.icon }}"></i> <span>{{ menu.title }}</span> </a> </li> {% else %} <li class="treeview {{ menu.class }}"> <a href="" class=""> <i class="fa {{ menu.icon }}"></i> <span>{{ menu.title }}</span> <span class="pull-right-container"> <span class="fa fa-angle-left pull-right"></span> </span> </a> {% endif %} {# 二級標簽 #} <ul class="treeview-menu"> {% for child_menu in menu.children %} {% if child_menu.menu_id != 1 %} <li class=""> <a href="{{ child_menu.url }}"> <i class="fa fa-circle-o"></i> {{ child_menu.title }} </a> </li> {% endif %} {% endfor %} </ul> </li> {% endfor %} {# fa-connectdevelop #} {# fa-code-fork #}

{% extends 'base.html' %} {% block css %} <style> .permission-area tr.parent { background-color: #cae7fd;; } .menu-body tr.active { background-color: #f1f7fd; border-left: 3px solid #fdc00f; } </style> {% endblock %} {% block content %} <div style="margin: 20px"> <div class="col-sm-3"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"><i class="fa fa-book"></i> 菜單管理 <a href="{% url 'menu_add' %}" class="btn btn-success btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-plus"></i> 新建</a> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>名稱</th> <th>圖標</th> <th>操作</th> </tr> </thead> <tbody class="menu-body"> {% for menu in all_menu %} <tr class=" {% if menu.id|safe == mid %} active {% endif %} "> <td><a href="?mid={{ menu.id }}">{{ menu.title }}</a></td> <td><i class="fa {{ menu.icon }} "></i></td> <td> <a href="{% url 'menu_edit' menu.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'menu_del' menu.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> <div class="col-sm-9"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"><i class="fa fa-cubes"></i> 權限管理 <a href="{% url 'multi_permissions' %}" class="btn btn-primary btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-scissors"></i> 批量操作</a> <a href="{% url 'permission_add' %}" class="btn btn-success btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-plus"></i> 新建</a> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>名稱</th> <th>URL</th> <th>URL別名</th> <th>菜單</th> <th>所屬菜單</th> <th>操作</th> </tr> </thead> <tbody class="permission-area"> {% for p_permission in all_permission_dict.values %} <tr class="parent" id="{{ p_permission.id }}"> <td class="title"> <i class="fa fa-caret-down"></i> {{ p_permission.title }} </td> <td>{{ p_permission.url }}</td> <td>{{ p_permission.name }}</td> <td> 是 </td> <td> {{ p_permission.menu__title }} </td> <td> <a href="{% url 'permission_edit' p_permission.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'permission_del' p_permission.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% for c_permission in p_permission.children %} <tr pid="{{ c_permission.parent_id }}"> <td>{{ c_permission.title }}</td> <td>{{ c_permission.url }}</td> <td>{{ c_permission.name }}</td> <td></td> <td></td> <td> <a href="{% url 'permission_edit' c_permission.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'permission_del' c_permission.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} {% endfor %} </tbody> </table> </div> </div> </div> {% endblock %} {% block js %} <script> $('.permission-area').on('click', '.parent .title', function () { var caret = $(this).find('i'); var id = $(this).parent().attr('id'); if (caret.hasClass('fa-caret-right')) { caret.removeClass('fa-caret-right').addClass('fa-caret-down'); $(this).parent().nextAll('tr[pid="' + id + '"]').removeClass('hide'); } else { caret.removeClass('fa-caret-down').addClass('fa-caret-right'); $(this).parent().nextAll('tr[pid="' + id + '"]').addClass('hide'); } }) </script> {% endblock %}

{% extends 'base.html' %} {% block content %} <div class="luffy-container"> <form method="post" action="?type=add"> {% csrf_token %} {{ add_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-binoculars" aria-hidden="true"></i> 待新建權限列表 <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 新建 </button> </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:路由系統中自動發現且數據庫中不存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序號</th> <th>名稱</th> <th>URL</th> <th>別名</th> <th>所屬菜單</th> <th>根權限</th> </tr> </thead> <tbody> {% for form in add_formset %} <tr> <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> </tr> {% endfor %} </tbody> </table> </div> </form> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-th-list" aria-hidden="true"></i> 待刪除權限列表 </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:數據庫中存在,但路由系統中不存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序號</th> <th>名稱</th> <th>URL</th> <th>別名</th> <th>父權限</th> <th>所屬菜單</th> <th>操作</th> </tr> </thead> <tbody> {% for form in del_formset %} <tr> {{ form.id }} <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> <td> <a href="{% url 'permission_del' form.id.value %}" style="color:#d9534f;"> <i class="fa fa-trash-o" aria-hidden="true"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> <form method="post" action="?type=update"> {% csrf_token %} {{ update_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-sitemap" aria-hidden="true"></i> 待更新權限列表 <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 更新 </button> </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:數據庫和路由系統都存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序號</th> <th>名稱</th> <th>URL</th> <th>別名</th> <th>父權限</th> <th>所屬菜單</th> <th>操作</th> </tr> </thead> <tbody> {% for form in update_formset %} <tr> {{ form.id }} <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> <td> <a href="{% url 'permission_del' form.id.value %}" style="color:#d9534f;"> <i class="fa fa-trash-o" aria-hidden="true"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> </form> </div> {% endblock %}

{% extends 'base.html' %} {% block content %} <div style="margin: 20px"> <h1>角色管理</h1> <a href="{% url "role_add" %}" class="btn btn-success">添加</a> <table class="table table-bordered table-hover" style="margin-top: 5px; border: red solid 2px;"> <thead> <tr> <th style="border: red solid 2px;">序號</th> <th style="border: red solid 2px;">名稱</th> <th style="border: red solid 2px;">操作</th> </tr> </thead> <tbody> {% for role in all_roles %} <tr> <td style="border: red solid 2px;">{{ forloop.counter }}</td> <td style="border: red solid 2px;">{{ role.name }}</td> <td style="border: red solid 2px;"> <a href="{% url 'role_edit' role.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'role_del' role.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
2.3 templatetags自定義標簽:
注意:自定義標簽時,應用中創建的文件夾名稱必須是:templatetags
from django import template # 創建一個注冊器 register = template.Library() from django.conf import settings import re # 返回menu_list到rbac/menu.html進行渲染標簽 @register.inclusion_tag('rbac/menu.html') def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) for key, item in menu_dict.items(): item["class"] = "" # url = item['url'] # if re.match('^{}$'.format(url), request.path_info): for child in item["children"]: if request.show_id == child["pk"]: item['class'] = 'active' child["class"] = "active" break return {"menu_dict": menu_dict} # 定義過濾器 @register.filter def haspermission(base_url, request): # 循環權限列表中的每一個字典 for item in request.session.get(settings.PERMISSION_SESSION_KEY): # 拼接item["url"]這個url,^……$ # print(item["url"]) # 這里有坑:正則表達式\d+匹配數字,所以base_url中樣式/customer/edit/數字/才能正確匹配 if re.search("^{}$".format(item["url"]), base_url): # 通過正則匹配,滿足則返回true return True # 不滿足條件則返回false return False # 權限分配時定義的標簽 @register.simple_tag def gen_role_url(request, rid): # request.POST或者GET都是不能被修改,通過調用copy方法實例化一個對象 params = request.GET.copy() # 修改_mutable的屬性為true params._mutable = True # 再次修改request的屬性值 params['rid'] = rid # 把字典數據類型的數據轉化成查詢字符串的格式類型 return params.urlencode()
2.4 view視圖管理:
2.4.1 views/distribute.py文件
# distribute.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac.models import Permission, Role from django.http import JsonResponse # 權限分配 def distribute_permissions2(request): """ 分配權限 :param request: :return: """ # request.get獲取uid的值 uid = request.GET.get('uid') # 通過uid的值獲取指定用戶user user = models.UserInfo.objects.filter(id=uid) # request.get獲取rid的值 rid = request.GET.get('rid') # 如果請求是POST方法時,且postType=role角色請求提交數據 if request.method == "POST" and request.POST.get('postType') == 'role': print(request.POST.getlist("roles")) # 用戶可以選擇多個角色,通過getlist獲取roles角色列表 l = request.POST.getlist("roles") # 用戶對象正向更新角色roles.set數據庫 user.first().roles.set(l) # 如果請求是POST方法時,且postType=permission權限請求提交數據 if request.method == "POST" and request.POST.get('postType') == 'permission': print(request.POST.getlist("permission_id")) # 獲取當前角色所選擇的所有權限 l = request.POST.getlist("permission_id") # 保存當前角色對應的權限 models.Role.objects.get(pk=rid).permissions.set(l) # 通過UserInfo獲取所有用戶 user_list = models.UserInfo.objects.all() # 獲取當前用戶的id與角色roles # user_has_roles = user.values('id', 'roles') # 通過Role獲取所有角色 role_list = models.Role.objects.all() print("uid", uid) if uid: # 獲取當前用戶所擁有的所有角色id,拿到的是一個<QuerySet [(1,)]>類型元組 role_id_list = models.UserInfo.objects.get(pk=uid).roles.all().values_list("pk") # item[0]獲取元組的第一個元素 role_id_list = [item[0] for item in role_id_list] if rid: # 寫法一 # 查看當前角色擁有的權限id per_id_list = models.Role.objects.filter(pk=rid).first().permissions.values_list("pk") # 寫法二 # per_id_list = models.Role.objects.filter(pk=rid).values_list("permissions__pk") else: # 獲取當前用戶所擁有的所有角色連表獲取所擁有角色的所有權限id,角色可能有相同權限,distinct去重 per_id_list = models.UserInfo.objects.get(pk=uid).roles.values_list("permissions__pk").distinct() # 同item[0] per_id_list = [item[0] for item in per_id_list] # print("per_id_list", per_id_list) return render(request, 'rbac/distribute_permissions2.html', locals()) # JS渲染標簽的需要的數據,通過ajax請求的 def permissions_tree(request): # 權限列表的屬性,QuerySet列表,前端生成標簽需要對priority排序 permissions = Permission.objects.values("pk", "title", "url", "menu__title", "menu__pk", "pid_id", "priority").order_by("priority") # print("permissions", permissions) # 返回JsonResponse數據,數據格式非字典類型,使用safe=False return JsonResponse(list(permissions), safe=False)
2.4.2 views/permission.py文件
# permission.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac import forms from django.db.models import Q # 權限信息列表(權限菜單管理) def menu_list(request): all_menu = models.Menu.objects.all() mid = request.GET.get('mid') if mid: permission_query = models.Permission.objects.filter(Q(menu_id=mid) | Q(pid__menu_id=mid)) else: permission_query = models.Permission.objects.all() all_permission = permission_query.values('id', 'url', 'title', 'name', 'menu_id', 'pid_id', 'menu__title') all_permission_dict = {} for item in all_permission: menu_id = item.get('menu_id') if menu_id: item['children'] = [] all_permission_dict[item['id']] = item for item in all_permission: pid = item.get('pid_id') if pid: all_permission_dict[pid]['children'].append(item) # print(all_permission_dict) return render(request, 'rbac/menu_list.html', {"all_menu": all_menu, 'all_permission_dict': all_permission_dict, 'mid': mid}) # 菜單權限信息列表添加與編輯 def menu(request, edit_id=None): obj = models.Menu.objects.filter(id=edit_id).first() form_obj = forms.MenuForm(instance=obj) if request.method == 'POST': form_obj = forms.MenuForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('menu_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 菜單權限信息刪除 def del_menu(request, del_id): obj = models.Menu.objects.get(pk=del_id) obj.delete() return redirect("menu_list") # 權限信息列表(權限管理) def permission(request, edit_id=None): obj = models.Permission.objects.filter(id=edit_id).first() form_obj = forms.PermissionForm(instance=obj) if request.method == 'POST': form_obj = forms.PermissionForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('menu_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 權限信息刪除 def del_permission(request, del_id): models.Permission.objects.filter(id=del_id).delete() return redirect(reverse('menu_list')) from django.forms import modelformset_factory, formset_factory from rbac import routes # 權限信息批量操作 def multi_permissions(request): """ 批量操作權限 :param request: :return: """ post_type = request.GET.get('type') # 更新和編輯用的 FormSet = modelformset_factory(models.Permission, forms.MultiPermissionForm, extra=0) # 增加用的 AddFormSet = formset_factory(forms.MultiPermissionForm, extra=0) permissions = models.Permission.objects.all() # 獲取路由系統中所有URL router_dict = routes.get_all_url_dict(ignore_namespace_list=['admin', 'rbac']) # 數據庫中的所有權限的別名 permissions_name_set = set([i.name for i in permissions]) # 路由系統中的所有權限的別名 router_name_set = set(router_dict.keys()) if request.method == 'POST' and post_type == 'add': add_formset = AddFormSet(request.POST) if add_formset.is_valid(): print(add_formset.cleaned_data) permission_obj_list = [models.Permission(**i) for i in add_formset.cleaned_data] query_list = models.Permission.objects.bulk_create(permission_obj_list) for i in query_list: permissions_name_set.add(i.name) add_name_set = router_name_set - permissions_name_set add_formset = AddFormSet(initial=[row for name, row in router_dict.items() if name in add_name_set]) del_name_set = permissions_name_set - router_name_set del_formset = FormSet(queryset=models.Permission.objects.filter(name__in=del_name_set)) update_name_set = permissions_name_set & router_name_set update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set)) if request.method == 'POST' and post_type == 'update': update_formset = FormSet(request.POST) if update_formset.is_valid(): update_formset.save() update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set)) return render( request, 'rbac/multi_permissions.html', { # 'del_formset': del_formset, # 'update_formset': update_formset, # 'add_formset': add_formset, } )
2.4.2 views/role.py文件
# role.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac import forms from django.db.models import Q # from rbac.routes import get_all_url_dict # 角色列表顯示 def role_list(request): all_roles = models.Role.objects.all() return render(request, 'rbac/role_list.html', {"all_roles": all_roles}) # 角色列表添加與編輯 def role(request, edit_id=None): obj = models.Role.objects.filter(id=edit_id).first() form_obj = forms.RoleForm(instance=obj) if request.method == 'POST': form_obj = forms.RoleForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('role_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 角色列表刪除 def del_role(request, del_id): models.Role.objects.filter(id=del_id).delete() return redirect(reverse('role_list'))
2.5 Django中自帶后天admin后台管理系統:這里可做前期調試用
# admin.py from django.contrib import admin from rbac import models class PermissionAdmin(admin.ModelAdmin): list_display = ["pk", "title", "url", "menu", "pid"] list_editable = ["url", "title", "pid"] ordering = ["-pk"] class RoleAdmin(admin.ModelAdmin): list_display = ["pk", "name"] list_editable = ["name"] ordering = ["-pk"] admin.site.register(models.UserInfo) admin.site.register(models.Role, RoleAdmin) admin.site.register(models.Permission, PermissionAdmin)
2.6 rbac中的表單設計:
# forms.py from django import forms from rbac import models from django.utils.safestring import mark_safe # 角色的Form class RoleForm(forms.ModelForm): class Meta: model = models.Role fields = ['name'] widgets = { 'name': forms.widgets.Input(attrs={"class": 'form-control'}) } ICON_LIST = [[i[0], mark_safe(i[1])] for i in [ ['fa-address-book', '<i aria-hidden="true" class="fa fa-address-book"></i>'], ['fa-address-book-o', '<i aria-hidden="true" class="fa fa-address-book-o"></i>'], ['fa-address-card', '<i aria-hidden="true" class="fa fa-address-card"></i>'], ['fa-address-card-o', '<i aria-hidden="true" class="fa fa-address-card-o"></i>'], ['fa-adjust', '<i aria-hidden="true" class="fa fa-adjust"></i>'], ['fa-american-sign-language-interpreting', '<i aria-hidden="true" class="fa fa-american-sign-language-interpreting"></i>'], ['fa-anchor', '<i aria-hidden="true" class="fa fa-anchor"></i>'], # ...省略 ['fa-window-restore', '<i aria-hidden="true" class="fa fa-window-restore"></i>'], ['fa-wrench', '<i aria-hidden="true" class="fa fa-wrench"></i>'] ]] # 菜單的Form class MenuForm(forms.ModelForm): class Meta: model = models.Menu fields = ['title', 'weight', 'icon', ] widgets = { 'title': forms.widgets.Input(attrs={"class": 'form-control'}), 'weight': forms.widgets.Input(attrs={"class": 'form-control'}), 'icon': forms.widgets.RadioSelect(choices=ICON_LIST), } class PermissionForm(forms.ModelForm): class Meta: model = models.Permission # fields = '__all__' fields = ['title', 'url', 'name', 'pid', 'menu', "priority"] help_texts = { "priority": "默認值1000表示優先級最高,優先級越高菜單顯示越靠下!" } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) # 批量權限使用的form class MultiPermissionForm(forms.ModelForm): class Meta: model = models.Permission fields = ['title', 'url', 'name', 'pid', 'menu'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({"class": "form-control"}) self.fields['pid'].choices = [(None, '-------')] + list( models.Permission.objects.filter(pid__isnull=True).exclude( menu__isnull=True).values_list('id', 'title')) def clean(self): menu = self.cleaned_data.get('menu') pid = self.cleaned_data.get('pid') if menu and pid: raise forms.ValidationError('菜單和根權限同時只能選擇一個') return self.cleaned_data
2.7 rbac中設計全局項目的權限中間件,包含權限白名單,登錄驗證,面包屑等配置:
# middlewares.py from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render, redirect, HttpResponse from django.conf import settings import re from rbac import models # 權限中間件 class PermissionMeddleWare(MiddlewareMixin): def process_request(self, request): # 對權限進行校驗 # 1. 當前訪問的URL current_url = request.path # print(current_url, ">>>>>>>>>>>>>>>>") # 白名單的判斷 for i in settings.WHITE_URL_LIST: if re.search(i, current_url): return None user = request.session.get("user") # 登錄驗證 if not user: return redirect("login") # 配置面包屑,首頁展示 request.menu_breadcrumb = [ # { # "title": "首頁", # "url": "/home/",} ] # 2. 獲取當前用戶的所有權限信息 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) # 3. 權限的校驗,循環驗證當前用戶訪問路徑是否有權限 for item in permission_list: url = item["url"] # 正則匹配驗證 if re.search("^{}$".format(url), current_url): # 1.驗證通過權限時,添加一個request對象的屬性show_id=item[pid],在自定義標簽時驗證是否是同一標簽下操作 request.show_id = item.get("pid", None) # 設置通過驗證后的操作的面包屑 if item["pid"] == item["pk"]: request.menu_breadcrumb.append({ "title": item.get("title", None), "url": request.path, }) else: obj = models.Permission.objects.filter(pk=item.get("pid", None)).first() if obj: l1 = [ {"title": obj.title, "url": obj.url}, {"title": item.get("title", None), "url": request.path} ] request.menu_breadcrumb.extend(l1) # 通過驗證返回None return None else: # 否則權限驗證失敗 # return HttpResponse("權限不夠!") return render(request, "permission_error.html")
2.8 項目數據庫的設計:
注意:此處項目中使用的Django自帶的認證系統:需要繼承類 AbstractUser
配置settings.py文件
當前應用下數據庫表中有ForeignKey或者MannyToMany字段關聯其他應用中的數據庫時,關聯時需要加相對應的應用名,例如:web應用關聯rbac中的UserInfo表(consultant = models.ForeignKey('rbac.UserInfo', verbose_name="銷售", blank=True, null=True)),后面web應用中出現這種情況

# 引用Django自帶的認證系統 AUTH_USER_MODEL = "rbac.UserInfo" # 設置認證失敗后跳轉的頁面,默認跳轉account/login LOGIN_URL = '/login/'

from django.db import models from django.contrib.auth.models import AbstractUser class Menu(models.Model): """ 菜單表 """ title = models.CharField(max_length=32, verbose_name='一級菜單', null=True, blank=True, default=None) icon = models.CharField(max_length=32, verbose_name='一級菜單圖標', null=True, blank=True, default=None) masterurl = models.CharField(max_length=128, verbose_name="一級菜單url", null=True, blank=True, default=None) weight = models.IntegerField(default=1, verbose_name='權重') class Meta: verbose_name_plural = '菜單表' verbose_name = '菜單表' def __str__(self): return self.title class Permission(models.Model): """ 權限表 """ title = models.CharField(max_length=32, verbose_name='標題', null=True, blank=True) url = models.CharField(max_length=32, verbose_name='權限') menu = models.ForeignKey(to="Menu", verbose_name="關聯菜單表", null=True, blank=True, default=None) pid = models.ForeignKey("self", verbose_name="自關聯權限表", null=True, blank=True, default=None) priority = models.IntegerField(verbose_name="優先級", null=True, blank=True, default=1000, help_text="優先級越高,越靠前") name = models.CharField(max_length=32, null=True, blank=True, unique=True, verbose_name='URL別名') class Meta: verbose_name_plural = '權限表' verbose_name = '權限表' ordering = ["priority", "menu"] def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名稱') permissions = models.ManyToManyField(to='Permission', verbose_name='角色所擁有的權限', blank=True) def __str__(self): return self.name class UserInfo(AbstractUser): """ 用戶表 """ username = models.CharField(max_length=32, verbose_name='用戶名', unique=True) password = models.CharField(max_length=128, verbose_name='密碼') telephone = models.CharField(max_length=32, null=True) roles = models.ManyToManyField(to='Role', verbose_name='用戶所擁有的角色', blank=True) def __str__(self): return self.username class Department(models.Model): """ 部門表 """ name = models.CharField(max_length=12, verbose_name='部門名稱') count = models.IntegerField(verbose_name='部門人數')
2.9 路由系統,此處后台系統中設計有點問題,待完善
# routes.py from django.conf import settings from django.utils.module_loading import import_string from django.urls import RegexURLResolver, RegexURLPattern from collections import OrderedDict def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict): for item in urlpatterns: if isinstance(item, RegexURLResolver): if pre_namespace: if item.namespace: namespace = "%s:%s" % (pre_namespace, item.namespace,) else: namespace = pre_namespace else: if item.namespace: namespace = item.namespace else: namespace = None recursion_urls(namespace, pre_url + item.regex.pattern, item.url_patterns, url_ordered_dict) else: if pre_namespace: name = "%s:%s" % (pre_namespace, item.name,) else: name = item.name if not item.name: raise Exception('URL路由中必須設置name屬性') url = pre_url + item._regex url_ordered_dict[name] = {'name': name, 'url': url.replace('^', '').replace('$', '')} def get_all_url_dict(ignore_namespace_list=None): """ 獲取路由中 :return: """ ignore_list = ignore_namespace_list or [] url_ordered_dict = OrderedDict() md = import_string(settings.ROOT_URLCONF) urlpatterns = [] for item in md.urlpatterns: if isinstance(item, RegexURLResolver) and item.namespace in ignore_list: continue urlpatterns.append(item) recursion_urls(None, "/", urlpatterns, url_ordered_dict) return url_ordered_dict
2.10 rbac中的路由分發:
# urls.py from django.conf.urls import url from rbac.views import role, permission, distribute urlpatterns = [ # 角色列表 url(r'^role/list/$', role.role_list, name='role_list'), # 角色添加 url(r'^role/add/$', role.role, name='role_add'), # 角色編輯 url(r'^role/edit/(\d+)$', role.role, name='role_edit'), # 角色刪除 url(r'^role/del/(\d+)$', role.del_role, name='role_del'), # 權限信息列表(權限菜單管理) url(r'^menu/list/$', permission.menu_list, name='menu_list'), # 菜單權限信息添加 url(r'^menu/add/$', permission.menu, name='menu_add'), # 菜單權限信息編輯 url(r'^menu/edit/(\d+)/$', permission.menu, name='menu_edit'), # 菜單權限信息刪除 url(r'^menu/del/(\d+)/$', permission.del_menu, name='menu_del'), # 權限信息列表(權限管理) # 權限添加 url(r'^permission/add/$', permission.permission, name='permission_add'), # 權限編輯 url(r'^permission/edit/(\d+)/$', permission.permission, name='permission_edit'), # 權限刪除 url(r'^permission/del/(\d+)/$', permission.del_permission, name='permission_del'), # 權限批量操作 url(r'^multi/permissions/$', permission.multi_permissions, name='multi_permissions'), # 權限分配 url(r'^distribute/permissions2/$', distribute.distribute_permissions2, name='distribute_permissions'), # 權限分配時,ajax請求數據 url(r'^permissions_tree/$', distribute.permissions_tree, name='distribute_permissions_list'), ]
三、web應用視圖相關設計
3.1 web應用圖例:
3.2 web中的路由分發配置:
# urls.py from django.conf.urls import url from django.contrib import admin from web.views import control, customer, enrollment, my_customer, record, student urlpatterns = [ # 注冊 url(r'^register/', control.register, name="register"), # 登錄 url(r'^login/', control.login, name="login"), # 驗證碼 url(r'^get_valid_img/', control.get_valid_img, name="get_valid_img"), # 退出 url(r'^logout/', control.logout, name="logout"), # 主頁 url(r'^home/', control.home, name="home"), # 公有客戶表 url(r'^customers/list/', customer.CustomerView.as_view(), name="customer"), # 添加公共客戶信息路徑 url(r'^customers/add/', customer.AddCustomer.as_view(), name='addcustomer'), # 修改公共客戶信息路徑 url(r'^customers/edit/(\d+)/', customer.EditCustomer.as_view(), name='editcustomer'), # 刪除公共客戶信息路徑 url(r'^customers/delete/(\d+)/', customer.DeleteCustomer.as_view(), name='deletecustomer'), # 私有客戶表 url(r'^my_customer/list/', my_customer.MyCustomerView.as_view(), name="my_customer"), # 添加私有客戶信息路徑 url(r'^my_customer/add/', customer.AddCustomer.as_view(), name='addmy_customer'), # 修改私有客戶信息路徑 url(r'^my_customer/edit/(\d+)/', customer.EditCustomer.as_view(), name='editmy_customer'), # 刪除私有客戶信息路徑 url(r'^my_customer/delete/(\d+)/', customer.DeleteCustomer.as_view(), name='deletemy_customer'), # 跟進記錄表,注意這里默認參數與reverse的用法 url(r'^my_record/list/(\d+)?', record.MyRecordView.as_view(), name="my_record"), # 添加跟進客戶信息 url(r'^my_record/add/', record.AddRecord.as_view(), name='addrecord'), # 修改跟進客戶信息 url(r'^my_record/edit/(\d+)/', record.EditRecord.as_view(), name='editrecord'), # 刪除跟進客戶信息 url(r'^my_record/delete/(\d+)/', record.DeleteRecord.as_view(), name='deleterecord'), # 報名記錄表 url(r'^my_enrollment/list/(\d+)?', enrollment.MyEnrollmentView.as_view(), name="my_enrollment"), # 添加報名客戶信息 url(r'^my_enrollment/add/', enrollment.AddEditEnrollment.as_view(), name='addenrollment'), # 修改報名客戶信息 url(r'^my_enrollment/edit/(\d+)/', enrollment.AddEditEnrollment.as_view(), name='editenrollment'), # 刪除報名客戶信息 url(r'^my_enrollment/delete/(\d+)/', enrollment.DeleteEnrollment.as_view(), name='deleteenrollment'), # 班級學習記錄信息 url(r'^class_record/', student.ClassRecordView.as_view(), name='class_record'), # 學生學習記錄信息 url(r'^study_record/(\d+)/', student.StudyRecordDeialView.as_view(), name='study_record'), # 分頁測試 url(r'^test/', control.test, name="test"), url(r'^profile/', control.profile, name="profile"), ]
3.3 數據庫中數據的批量操作:

# 添加數據.py import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mydjango_crm_version_1.settings") import django django.setup() from web import models import random # 客戶表添加記錄 l1 = [] for i in range(1, 101): obj = models.Customer( qq="123%s" % i, name="an%s" % i, sex=random.choice(("男", "女")), course=random.choice(("Linux中高級", "Python高級全棧開發")), source=random.choice(("qq群", "內部轉介紹", "官方網站")), # consultant=random.choice(models.Customer.consultant.id) ) l1.append(obj) models.Customer.objects.bulk_create(l1) # 報名表添加記錄 # l2 = [] # customers_obj = models.Customer.objects.all() # school_obj = models.Campuses.objects.all() # enrolment_class_obj = models.ClassList.objects.all() # customers = random.sample(list(customers_obj), k=10) # print(customers_obj,"11111111111111") # print(school_obj,"22222222222222") # print(enrolment_class_obj,"3333333333333") # for i in range(1, 11): # obj = models.Enrollment( # customer=customers[i-1], # why_us="123%s" % i, # your_expectation="123%s" % i, # memo="123%s" % i, # school=random.choice(school_obj), # enrolment_class=random.choice(enrolment_class_obj), # ) # l2.append(obj) # models.Enrollment.objects.bulk_create(l2)
3.4 數據分頁模板設計:
# page.py # 函數版 def pagenation(base_url, current_page_num, total_counts, per_page_counts=10, page_number=5): ''' total_counts數據總數 per_page_counts每頁分多少條數據 page_number = 頁碼顯示多少個 current_page_num 當前頁 :return: ''' # all_objs_list = models.Customer.objects.all() # total_counts = all_objs_list.count() # per_page_counts = 10 # page_number = 5 try: current_page_num = int(current_page_num) except Exception: current_page_num = 1 half_page_range = page_number // 2 # 計算總頁數 page_number_count, a = divmod(total_counts, per_page_counts) if current_page_num < 1: current_page_num = 1 if a: page_number_count += 1 if current_page_num > page_number_count: current_page_num = page_number_count start_num = (current_page_num - 1) * 10 end_num = current_page_num * 10 if page_number_count <= page_number: page_start = 1 page_end = page_number_count else: if current_page_num <= half_page_range: page_start = 1 page_end = page_number elif current_page_num + half_page_range >= page_number_count: page_start = page_number_count - page_number + 1 page_end = page_number_count else: page_start = current_page_num - half_page_range page_end = current_page_num + half_page_range tab_html = '' tab_html += '<nav aria-label="Page navigation"><ul class="pagination">' # 上一頁 if current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a><>' else: previous_page = '<li><a href="{0}?page={1}" aria-label="Previous" ><span aria-hidden="true">«</span></a><>'.format( base_url, current_page_num - 1) tab_html += previous_page for i in range(page_start, page_end + 1): if current_page_num == i: one_tag = '<li class="active"><a href="{0}?page={1}">{1}</a><>'.format(base_url, i) else: one_tag = '<li><a href="{0}?page={1}">{1}</a><>'.format(base_url, i) tab_html += one_tag # 下一頁 if current_page_num == page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a><>' else: next_page = '<li><a href="{0}?page={1}" aria-label="Next"><span aria-hidden="true">»</span></a><>'.format( base_url, current_page_num + 1) tab_html += next_page tab_html += '</ul></nav>' return tab_html, start_num, end_num import copy # 自定義分頁 # 官方推薦,頁碼數為奇數 class PageNation: def __init__(self, base_url, current_page_num, total_counts, request, per_page_counts=10, page_number=5): ''' :param base_url: 分頁展示信息的基礎路徑 :param current_page_num: 當前頁頁碼 :param total_counts: 總的數據量 :param per_page_counts: 每頁展示的數據量 :param page_number: 顯示頁碼數 ''' self.base_url = base_url # 獲取當前url 例如:/index/ self.current_page_num = current_page_num # 當前頁 self.total_counts = total_counts # 總的數據量 self.per_page_counts = per_page_counts # 每頁的數據量 self.page_number = page_number # 分頁器分多少頁 self.request = request try: self.current_page_num = int(self.current_page_num) # 獲取當前頁 except Exception: self.current_page_num = 1 # 捕獲異常:防止輸入錯誤 half_page_range = self.page_number // 2 # 總分頁獲取中間值,然后拿到左邊的個數 # 計算總頁數 self.page_number_count, a = divmod(self.total_counts, self.per_page_counts) if self.current_page_num < 1: self.current_page_num = 1 if a: self.page_number_count += 1 # 存在余數的情況則增加一頁 if self.current_page_num > self.page_number_count: self.current_page_num = self.page_number_count if self.page_number_count <= self.page_number: # 頁數少於分頁器指定頁數 self.page_start = 1 self.page_end = self.page_number_count else: if self.current_page_num <= half_page_range: # 當前頁數少於第一個分頁器中頁數的一半時 self.page_start = 1 self.page_end = page_number elif self.current_page_num + half_page_range >= self.page_number_count: # 當前頁數多於最后一個分頁器頁數的一半 self.page_start = self.page_number_count - self.page_number + 1 self.page_end = self.page_number_count else: self.page_start = self.current_page_num - half_page_range self.page_end = self.current_page_num + half_page_range self.query_dict = copy.deepcopy(self.request.GET) # 數據切片依據,起始位置 @property def start_num(self): start_num = (self.current_page_num - 1) * self.per_page_counts if self.current_page_num == 0: start_num = 0 return start_num # 數據切片依據,終止位置 @property def end_num(self): end_num = self.current_page_num * self.per_page_counts return end_num # 獲取當前查詢字符串 def get_querystr(self, i): self.query_dict["page"] = i query_str = self.query_dict.urlencode() # django轉換字典類型為查詢字符串 return query_str # 拼接HTMl標簽 def page_html(self): tab_html = '' tab_html += '<nav aria-label="Page navigation" class="pull-right"><ul class="pagination">' # 首頁 query_str = self.get_querystr(1) first_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">首頁</span></a></li>'.format( self.base_url, query_str) tab_html += first_page # 上一頁 if self.current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>' else: query_str = self.get_querystr(self.current_page_num - 1) # 獲取上一頁 previous_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>'.format( self.base_url, query_str) tab_html += previous_page for i in range(self.page_start, self.page_end + 1): query_str = self.get_querystr(i) if self.current_page_num == i: one_tag = '<li class="active"><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i, query_str) else: one_tag = '<li><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i, query_str) tab_html += one_tag # 下一頁 if self.current_page_num == self.page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li>' else: query_str = self.get_querystr(self.current_page_num + 1) # 獲取下一頁 next_page = '<li><a href="{0}?{1}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format( self.base_url, query_str) tab_html += next_page # 尾頁 query_str = self.get_querystr(self.page_number_count) last_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">尾頁</span></a></li>'.format( self.base_url, query_str) tab_html += last_page tab_html += '</ul></nav>' return tab_html
3.5 web中的數據庫表:

# models.py from django.db import models from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User from multiselectfield import MultiSelectField # 安裝:pip install django-multiselectfield,針對choices多選用的 course_choices = (('LinuxL', 'Linux中高級'), ('PythonFullStack', 'Python高級全棧開發'),) class_type_choices = (('fulltime', '脫產班',), ('online', '網絡班'), ('weekend', '周末班',),) source_type = (('qq', "qq群"), ('referral', "內部轉介紹"), ('website', "官方網站"), ('baidu_ads', "百度推廣"), ('office_direct', "直接上門"), ('WoM', "口碑"), ('public_class', "公開課"), ('website_luffy', "路飛官網"), ('others', "其它"),) enroll_status_choices = (('signed', "已報名"), ('unregistered', "未報名"), ('studying', '學習中'), ('paid_in_full', "學費已交齊")) seek_status_choices = (('A', '近期無報名計划'), ('B', '1個月內報名'), ('C', '2周內報名'), ('D', '1周內報名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '無效'),) pay_type_choices = (('deposit', "訂金/報名費"), ('tuition', "學費"), ('transfer', "轉班"), ('dropout', "退學"), ('refund', "退款"),) attendance_choices = (('checked', "已簽到"), ('vacate', "請假"), ('late', "遲到"), ('absence', "缺勤"), ('leave_early', "早退"),) 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'),) class Customer(models.Model): """ 客戶表(最開始的時候大家都是客戶,銷售就不停的撩你,你還沒交錢就是個客戶) """ qq = models.CharField(verbose_name='QQ', max_length=64, unique=True, help_text='QQ號必須唯一') qq_name = models.CharField('QQ昵稱', max_length=64, blank=True, null=True) name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='學員報名后,請改為真實姓名') sex_type = (('male', '男'), ('female', '女')) sex = models.CharField("性別", choices=sex_type, max_length=16, default='male', blank=True, null=True) # 存的是male或者female,字符串 birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.CharField('手機號', blank=True, null=True, max_length=32) # phone = models.CharField('手機號', blank=True, null=True) source = models.CharField('客戶來源', max_length=64, choices=source_type, default='qq') introduce_from = models.ForeignKey('self', verbose_name="轉介紹自學員", blank=True, null=True) # self指的就是自己這個表,和下面寫法是一樣的效果 # introduce_from = models.ForeignKey('Customer', verbose_name="轉介紹自學員", blank=True, null=True,on_delete=models.CASCADE) # pip install django-multiselectfield course = MultiSelectField("咨詢課程", choices=course_choices) # 多選,並且存成一個列表的格式 # course = models.CharField("咨詢課程", choices=course_choices) #如果你不想用上面的多選功能,可以使用Charfield來存 class_type = models.CharField("班級類型", max_length=64, choices=class_type_choices, default='fulltime', blank=True, null=True) customer_note = models.TextField("客戶備注", blank=True, null=True, ) status = models.CharField("狀態", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="選擇客戶此時的狀態") # help_text這種參數基本都是針對admin應用里面用的 date = models.DateTimeField("咨詢日期", auto_now_add=True) last_consult_date = models.DateField("最后跟進日期", auto_now_add=True) # 考核銷售的跟進情況,如果多天沒有跟進,會影響銷售的績效等 next_date = models.DateField("預計再次跟進時間", blank=True, null=True) # 銷售自己大概記錄一下自己下一次會什么時候跟進,也沒啥用 # 用戶表中存放的是自己公司的所有員工。 consultant = models.ForeignKey('rbac.UserInfo', verbose_name="銷售", blank=True, null=True) # 一個客戶可以報多個班,報個脫產班,再報個周末班等,所以是多對多。 class_list = models.ManyToManyField('ClassList', verbose_name="已報班級", blank=True) def __str__(self): return self.name + ":" + self.qq # 主要__str__最好是個字符串昂,不然你會遇到很多的坑,還有我們返回的這兩個字段填寫數據的時候必須寫上數據,必然相加會報錯,null類型和str類型不能相加等錯誤信息。 def get_classlist(self): # 當我們通過self.get_classlist的時候,就拿到了所有的班級信息,前端顯示的時候用 l = [] for cls in self.class_list.all(): l.append(str(cls)) # return mark_safe(",".join(l)) #純文本,不用mark_safe也可以昂 return ",".join(l) # 純文本,不用mark_safe也可以昂 class Campuses(models.Model): """ 校區表 """ name = models.CharField(verbose_name='校區', max_length=64) address = models.CharField(verbose_name='詳細地址', max_length=512, blank=True, null=True) def __str__(self): return self.name class ClassList(models.Model): """ 班級表hide """ course = models.CharField("課程名稱", max_length=64, choices=course_choices) semester = models.IntegerField("學期") # python20期等 campuses = models.ForeignKey('Campuses', verbose_name="校區", on_delete=models.CASCADE) price = models.IntegerField("學費", default=10000) memo = models.CharField('說明', blank=True, null=True, max_length=100) start_date = models.DateField("開班日期") graduate_date = models.DateField("結業日期", blank=True, null=True) # 不一定什么時候結業,哈哈,所以可為空 # contract = models.ForeignKey('ContractTemplate', verbose_name="選擇合同模版", blank=True, null=True,on_delete=models.CASCADE) teachers = models.ManyToManyField('rbac.UserInfo', verbose_name="老師") # 對了,還有一點,如果你用的django2版本的,那么外鍵字段都需要自行寫上on_delete=models.CASCADE class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班額及類型', blank=True, null=True) class Meta: unique_together = ("course", "semester", 'campuses') def __str__(self): return "{}{}({})".format(self.get_course_display(), self.semester, self.campuses) class ConsultRecord(models.Model): """ 跟進記錄表 """ customer = models.ForeignKey('Customer', verbose_name="所咨詢客戶") note = models.TextField(verbose_name="跟進內容...") status = models.CharField("跟進狀態", max_length=8, choices=seek_status_choices, help_text="選擇客戶此時的狀態") consultant = models.ForeignKey("rbac.UserInfo", verbose_name="跟進人", related_name='records') date = models.DateTimeField("跟進日期", auto_now_add=True) delete_status = models.BooleanField(verbose_name='刪除狀態', default=False) class Enrollment(models.Model): """ 報名表 """ customer = models.ForeignKey('Customer', verbose_name='客戶名稱') why_us = models.TextField("為什么報名", max_length=1024, default=None, blank=True, null=True) your_expectation = models.TextField("學完想達到的具體期望", max_length=1024, blank=True, null=True) enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="報名日期") memo = models.TextField('備注', blank=True, null=True) delete_status = models.BooleanField(verbose_name='刪除狀態', default=False) school = models.ForeignKey('Campuses') # 校區表 enrolment_class = models.ForeignKey("ClassList", verbose_name="所報班級") class Meta: unique_together = ('enrolment_class', 'customer') class Student(models.Model): """ 學生表(已報名) """ customer = models.OneToOneField(verbose_name='客戶信息', to='Customer', on_delete=models.CASCADE, null=True, blank=True) class_list = models.ManyToManyField(verbose_name="已報班級", to='ClassList', blank=True, related_name="students") 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", on_delete=models.CASCADE) day_num = models.IntegerField(verbose_name="節次", help_text=u"此處填寫第幾節課或第幾天課程...,必須為數字") teacher = models.ForeignKey(verbose_name="講師", to='rbac.UserInfo', on_delete=models.CASCADE) 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): ''' 學生學習記錄 ''' student = models.ForeignKey(verbose_name="學員", to='Student', on_delete=models.CASCADE) classstudyrecord = models.ForeignKey(verbose_name="第幾天課程", to="ClassStudyRecord", on_delete=models.CASCADE) 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) class Meta: unique_together = ["student", "classstudyrecord"]
3.6 Django后台管理admin,同rbac用於前期調試:
from django.contrib import admin from web import models # 自定義客戶表的Admin class CustomerAdmin(admin.ModelAdmin): list_display = ["qq_name", "name", "source", "introduce_from", "class_type", "status", ] list_editable = ["name", "source", "introduce_from", "class_type", "status", ] search_fields = ('name',) # 定制搜索列表 def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(CustomerAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(pk=search_term_as_int) except: pass return queryset, use_distinct # 自定義學生學習記錄表的Admin class StudentStudyRecordAdmin(admin.ModelAdmin): list_display = ['student', 'classstudyrecord', 'record', 'score'] list_editable = ['record', 'score'] admin.site.register(models.Customer, CustomerAdmin) admin.site.register(models.Campuses) admin.site.register(models.ClassList) admin.site.register(models.StudentStudyRecord, StudentStudyRecordAdmin) admin.site.register(models.ClassStudyRecord) admin.site.register(models.Student)
3.7 web/views視圖文件 :
3.7.1 control.py文件:管路驗證碼,注冊,登錄,主頁
# control.py from django.shortcuts import render, HttpResponse, redirect from rbac.models import Permission, UserInfo from web.models import Customer import random from PIL import Image, ImageDraw, ImageFont from django.contrib import auth from django.http import JsonResponse from django.contrib.auth.decorators import login_required from web.forms.register_form import MyForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator from rbac.server import init_permission # 驗證碼 def get_valid_img(request): def get_random_color(): return (random.randint(0, 110), random.randint(0, 110), random.randint(0, 110)) def get_random_back_color(): return (random.randint(110, 255), random.randint(110, 255), random.randint(110, 255)) # img_obj = Image.new('RGB', (200, 34), get_random_back_color()) # 圖片對象 img_obj = Image.new('RGB', (140, 34), get_random_back_color()) # 此處使用register1.html文件 draw_obj = ImageDraw.Draw(img_obj) # 通過圖片對象生成一個畫筆對象 # font_path = os.path.join(settings.BASE_DIR, "statics/font/cerepf__.ttf") # 獲取字體,注意有些字體文件不能識別數字,所以如果數字顯示不出來,就換個字體 font_obj = ImageFont.truetype("statics/font/cerepf__.ttf", 28) # 創建字體對象 sum_str = '' # 這個數據就是用戶需要輸入的驗證碼的內容 for i in range(1, 5): a = random.choice( [str(random.randint(0, 9)), chr(random.randint(97, 122)), chr(random.randint(65, 90))]) # 4 a 5 D # draw_obj.text((i * 35, 0), a, fill=get_random_color(), font=font_obj) # 此處使用register.html draw_obj.text((i * 22, 0), a, fill=get_random_color(), font=font_obj) # 此處使用register1.html sum_str += a print(sum_str) # draw_obj.text((64, 10), sum_str, fill=get_random_color(), font=font_obj) # 通過畫筆對象,添加文字 width = 200 height = 34 # 添加噪線 for i in range(5): # 添加了5條線 # 一個坐標表示一個點,兩個點就可以連成一條線 x1 = random.randint(0, width) x2 = random.randint(0, width) y1 = random.randint(0, height) y2 = random.randint(0, height) draw_obj.line((x1, y1, x2, y2), fill=get_random_color()) # 添加噪點 for i in range(10): # 這是添加點,50個點 draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) # 下面是添加很小的弧線,看上去類似於一個點,50個小弧線 x = random.randint(0, width) y = random.randint(0, height) draw_obj.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) # x, y是弧線的起始點位置,x + 4, y + 4是弧線的結束點位置 from io import BytesIO f = BytesIO() # 操作內存的把手 img_obj.save(f, 'png') data = f.getvalue() # 存這個驗證碼的方式1:賦值給全局變量的簡單測試 # global valid_str # valid_str = sum_str # 方式2:將驗證碼存在各用戶自己的session中,session的應用其實還有很多 request.session['valid_str'] = sum_str return HttpResponse(data) # 注冊 def register(request): state = None if request.method == "GET": form_obj = MyForm() return render(request, "login_register/register1.html", {"form_obj": form_obj}) else: data = request.POST form_obj = MyForm(data) if form_obj.is_valid(): print(form_obj.cleaned_data) if UserInfo.objects.filter(username=form_obj.cleaned_data.get("username")): state = "用戶已存在!" return HttpResponse(state) else: pwd = form_obj.cleaned_data.get("password") r_pwd = form_obj.cleaned_data.get("r_password") if pwd == r_pwd: del form_obj.cleaned_data["r_password"] UserInfo.objects.create_user(**form_obj.cleaned_data) return redirect("login") else: error_pwd = "兩次輸入密碼不一致!" return render(request, "login_register/register1.html", {"form_obj": form_obj, "error_pwd": error_pwd}) else: return render(request, "login_register/register1.html", {"form_obj": form_obj}) # 登錄 def login(request): ''' 響應的狀態碼: 成功:1000 驗證碼失敗:1001 用戶名密碼失敗:1002 :param request: :return: ''' response_msg = {'code': None, 'msg': None} if request.method == "GET": return render(request, "login_register/login1.html") else: captcha = request.POST.get("captcha") user = request.POST.get('username') pwd = request.POST.get('password') if captcha.upper() == request.session.get("valid_str").upper(): user_obj = auth.authenticate(username=user, password=pwd) if user_obj: auth.login(request, user_obj) response_msg['code'] = 1000 response_msg['msg'] = '登錄成功!' request.session["user"] = user_obj.username # 配置一個user用戶的session init_permission.init_permission(request, user_obj) # 在rbac中初始化session配置 else: response_msg['code'] = 1002 response_msg['msg'] = '用戶名或者密碼輸入有誤!' else: response_msg['code'] = 1001 response_msg['msg'] = '驗證碼輸入有誤!' return JsonResponse(response_msg) # 主頁 @login_required def home(request): return render(request, "home.html") # profile頁(修改密碼) def profile(request): status = None if request.method == "GET": return render(request, "profile.html") else: user = request.user password = request.POST.get("password") r1_password = request.POST.get("r1_password") r2_password = request.POST.get("r2_password") # print(user) # print(password) # print(r1_password) # print(r2_password) if user.check_password(password): # 驗證舊密碼是否正確 if r1_password: if r1_password == r2_password: user.set_password(r1_password) # 修改密碼 user.save() # 保存修改 return redirect("login") else: status = "兩次密碼不一致!" else: status = "密碼不能為空!" else: status = "密碼錯誤" return HttpResponse(status) # 退出 @login_required def logout(request): auth.logout(request) return redirect("login") # 分頁測試 def test(request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page_num = request.GET.get('page', 1) if wd: q = Q() # 實例化一個Q對象 q.children.append((condition, wd)) # all_data = models.Customer.objects.filter(Q(qq__contains=wd)|Q(name__contains=wd)) # q.connector = 'and' # 指定條件連接符號 # q.children.append((condition1, wd1)) # q.children.append((condition2, wd2)) all_data = Customer.objects.filter(q) else: all_data = Customer.objects.all() per_page_counts = 10 # 每頁顯示10條 page_number = 5 # 總共顯示5個頁碼 total_count = all_data.count() # 總數據個數 # ret_html, start_num, end_num = page.pagenation(request.path, current_page_num, total_count, per_page_counts, # page_number) p_obj = page.PageNation(request.path, current_page_num, total_count, request, per_page_counts, page_number) try: all_data = all_data[p_obj.start_num:p_obj.end_num] except Exception: pass return render(request, 'test.html', {'all_data': all_data, 'ret_html': p_obj.page_html()})
3.7.2 customer客戶列表,顯示當前所有的公有用戶列表
# customer.py from django.shortcuts import render, HttpResponse, redirect,reverse from web import models import random import re from django.contrib.auth.decorators import login_required from web.forms.customer_form import CustomerModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 公有客戶表 # @login_required # def customer(request): class CustomerView(View): @method_decorator(login_required) def get(self, request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page = request.GET.get("page", 1) if wd: q = Q() q.children.append((condition, wd)) all_customers = models.Customer.objects.all().filter(q, consultant__isnull=True).order_by("-pk") else: all_customers = models.Customer.objects.all().filter(consultant__isnull=True).order_by("-pk") per_page_counts = 8 # 每頁顯示多少條數據 page_number = 7 # 顯示多少頁 total_number = all_customers.count() # 總數據量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分頁 try: all_customers = all_customers[page_obj.start_num: page_obj.end_num] # 對多有信息切片顯示 except Exception as e: print(e) ret_html = page_obj.page_html() # 獲取html分頁字符串 return render(request, "customer/customer.html", {"all_customers": all_customers, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": self.status = request.POST.get("action-update") # print(self.status) # studying # print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("customer") else: # return HttpResponse("異常操作!") return redirect("customer") else: # return HttpResponse("異常操作!") return redirect("customer") # 批量刪除 def batch_delete(self, request): models.Customer.objects.filter(pk__in=self.data).delete() # 批量更新客戶狀態 def batch_update(self, request): models.Customer.objects.filter(pk__in=self.data).update(status=self.status) # 批量公轉私 def batch_reverse_gs(self, request): models.Customer.objects.filter(pk__in=self.data).update(consultant=request.user) # 添加數據 class AddCustomer(View): # 獲取添加頁面 def get(self, request): form_obj = CustomerModelForm() return render(request, "customer/add_customers.html", {"form_obj": form_obj}) def post(self, request): form_obj = CustomerModelForm(request.POST) if form_obj.is_valid(): form_obj.save() # 創建一條新數據 reverse_url = reversed("addcustomer") # 通過路徑反轉,獲取addcustomer對應的路徑 # print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer") else: return render(request, "customer/add_customers.html", {"form_obj": form_obj}) # 修改數據 class EditCustomer(View): def get(self, request, pk): # url分頁器自動獲取到id值,也可以指定參數的形式傳出來 custome_obj = models.Customer.objects.filter(pk=pk).first() # 獲取customer對象 form_obj = CustomerModelForm(instance=custome_obj) return render(request, "customer/edit_customers.html", {"form_obj": form_obj}) def post(self, request, pk): custome_obj = models.Customer.objects.filter(pk=pk).first() form_obj = CustomerModelForm(request.POST, instance=custome_obj) # 不指定對象,modelform會新創建一條記錄 if form_obj.is_valid(): form_obj.save() # 更新數據 reverse_url = reverse("editcustomer", args=(pk,)) print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer") else: return render(request, "customer/edit_customers.html", {"form_obj": form_obj}) # 刪除信息 class DeleteCustomer(View): def get(self, request, pk): print(pk) customer = models.Customer.objects.filter(pk=pk).first() print(customer) models.Customer.objects.filter(pk=pk).delete() reverse_url = reverse("deletecustomer", args=(pk,)) print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer")
3.7.3 enrollment.py報名信息表:客戶報名
# enrollment.py from django.shortcuts import render, HttpResponse, redirect, reverse from web import models from django.contrib.auth.decorators import login_required from web.forms.enrollment_form import EnrollmentModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 報名記錄表 class MyEnrollmentView(View): def get(self, request, pk=None): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") opt = (('A', '近期無報名計划'), ('B', '1個月內報名'), ('C', '2周內報名'), ('D', '1周內報名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '無效'), ) current_page = request.GET.get("page", 1) my_enrollments = models.Enrollment.objects.filter().order_by("-pk") if pk: print(pk) my_enrollments = models.Enrollment.objects.filter(customer_id=pk) # 查詢字段 if wd: q = Q() status_list = [] # 查詢列表,存在多個條件,放在列表中 if condition == "status": # 通過跟進狀態查詢 for i in opt: if wd in i[1]: status_list.append(i[0]) condition = condition + "__in" q.children.append((condition, status_list)) else: # 通過咨詢客戶查詢 condition += "__name" # 咨詢客戶 condition = condition + "__contains" q.children.append((condition, wd)) my_enrollments = my_enrollments.filter(q) status_list.clear() per_page_counts = 5 # 每頁顯示多少條數據 page_number = 11 # 顯示多少頁 total_number = my_enrollments.count() # 總數據量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分頁 try: my_enrollments = my_enrollments[page_obj.start_num: page_obj.end_num] # 對多有信息切片顯示 except Exception as e: print(e) ret_html = page_obj.page_html() # 獲取html分頁字符串 return render(request, "enrollment/my_enrollment.html", {"my_enrollments": my_enrollments, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request, pk=None): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": # 批量更新 self.status = request.POST.get("action-update") print(self.status) print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect(reverse("my_enrollment", args=(pk,))) else: # return HttpResponse("異常操作!") return redirect("my_enrollment", args=(pk,)) else: # return HttpResponse("異常操作!") return redirect("my_enrollment", args=(pk,)) # 批量刪除 def batch_delete(self, request): models.Enrollment.objects.filter(pk__in=self.data).delete() # 批量更新跟進狀態 def batch_update(self, request): models.Enrollment.objects.filter(pk__in=self.data).update(enrolment_class_id=int(self.status)) # 添加與修改報名數據記錄 class AddEditEnrollment(View): # 獲取添加頁面 def get(self, request, pk=None): enrollment_obj = models.Enrollment.objects.filter(pk=pk).first() form_obj = EnrollmentModelForm(instance=enrollment_obj) return render(request, "enrollment/add_enrollments.html", {"form_obj": form_obj}) def post(self, request, pk=None): enrollment_obj = models.Enrollment.objects.filter(pk=pk).first() form_obj = EnrollmentModelForm(request.POST, instance=enrollment_obj) if form_obj.is_valid(): form_obj.save() return redirect("my_enrollment") else: return render(request, "enrollment/add_enrollments.html", {"form_obj": form_obj}) # 刪除報名數據記錄 class DeleteEnrollment(View): def get(self, request, pk): print(pk) record = models.Enrollment.objects.filter(pk=pk).first() print(record) models.Enrollment.objects.filter(pk=pk).delete() return redirect("my_enrollment")
3.7.4 my_customer.py這里設計私有客戶:
# my_customer.py from django.shortcuts import render, HttpResponse, redirect from web import models from django.contrib.auth.decorators import login_required from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 私有客戶表表 # @login_required # def my_customer(request): class MyCustomerView(View): def get(self, request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page = request.GET.get("page", 1) my_customers = models.Customer.objects.all().filter(consultant=request.user) if wd: q = Q() q.children.append((condition, wd)) my_customers = my_customers.filter(q) per_page_counts = 5 # 每頁顯示多少條數據 page_number = 11 # 顯示多少頁 total_number = my_customers.count() # 總數據量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分頁 try: my_customers = my_customers[page_obj.start_num: page_obj.end_num] # 對多有信息切片顯示 except Exception as e: print(e) ret_html = page_obj.page_html() # 獲取html分頁字符串 return render(request, "customer/my_customer.html", {"my_customers": my_customers, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": self.status = request.POST.get("action-update") # print(self.status) # studying print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("customer") else: # return HttpResponse("異常操作!") return redirect("customer") else: # return HttpResponse("異常操作!") return redirect("customer") # 批量刪除 def batch_delete(self, request): models.Customer.objects.filter(pk__in=self.data).delete() # 批量更新客戶狀態 def batch_update(self, request): models.Customer.objects.filter(pk__in=self.data).update(status=self.status) # 批量私轉公 def batch_reverse_gs(self, request): models.Customer.objects.filter(pk__in=self.data).update(consultant=None)
3.7.5 record.py客戶跟進記錄:
# record.py from django.shortcuts import render, HttpResponse, redirect from web import models from django.contrib.auth.decorators import login_required from web.forms.record_form import ConsultRecordModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 跟進記錄表 class MyRecordView(View): def get(self, request, pk=None): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") opt = (('A', '近期無報名計划'), ('B', '1個月內報名'), ('C', '2周內報名'), ('D', '1周內報名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '無效'), ) current_page = request.GET.get("page", 1) my_records = models.ConsultRecord.objects.filter(consultant=request.user) if pk: print(pk) my_records = models.ConsultRecord.objects.filter(customer_id=pk) # 查詢字段 if wd: q = Q() status_list = [] # 查詢列表,存在多個條件,放在列表中 if condition == "status": # 通過跟進狀態查詢 for i in opt: if wd in i[1]: status_list.append(i[0]) condition = condition + "__in" q.children.append((condition, status_list)) else: # 通過咨詢客戶查詢 condition += "__name" # 咨詢客戶 condition = condition + "__contains" q.children.append((condition, wd)) my_records = my_records.filter(q) status_list.clear() per_page_counts = 5 # 每頁顯示多少條數據 page_number = 11 # 顯示多少頁 total_number = my_records.count() # 總數據量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分頁 try: my_records = my_records[page_obj.start_num: page_obj.end_num] # 對多有信息切片顯示 except Exception as e: print(e) ret_html = page_obj.page_html() # 獲取html分頁字符串 return render(request, "record/my_record.html", {"my_records": my_records, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request, pk=None): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": # 批量更新 self.status = request.POST.get("action-update") print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("my_record") else: # return HttpResponse("異常操作!") return redirect("my_record") else: # return HttpResponse("異常操作!") return redirect("my_record") # 批量刪除 def batch_delete(self, request): models.ConsultRecord.objects.filter(pk__in=self.data).delete() # 批量更新跟進狀態 def batch_update(self, request): models.ConsultRecord.objects.filter(pk__in=self.data).update(status=self.status) # 添加跟進數據記錄 class AddRecord(View): # 獲取添加頁面 def get(self, request): form_obj = ConsultRecordModelForm(request) return render(request, "record/add_records.html", {"form_obj": form_obj}) def post(self, request): form_obj = ConsultRecordModelForm(request, request.POST) if form_obj.is_valid(): form_obj.save() # 創建一條新數據 return redirect("my_record") else: return render(request, "record/add_records.html", {"form_obj": form_obj}) # 修改跟進數據記錄 class EditRecord(View): def get(self, request, pk): record_obj = models.ConsultRecord.objects.filter(pk=pk).first() form_obj = ConsultRecordModelForm(request, instance=record_obj) return render(request, "record/edit_records.html", {"form_obj": form_obj}) def post(self, request, pk): record_obj = models.ConsultRecord.objects.filter(pk=pk).first() form_obj = ConsultRecordModelForm(request, request.POST, instance=record_obj) # 更新記錄,指定instance if form_obj.is_valid(): form_obj.save() # 更新數據 return redirect("my_record") else: return render(request, "record/edit_records.html", {"form_obj": form_obj}) # 刪除跟進數據記錄 class DeleteRecord(View): def get(self, request, pk): print(pk) record = models.ConsultRecord.objects.filter(pk=pk).first() print(record) models.ConsultRecord.objects.filter(pk=pk).delete() return redirect("my_record")
3.7.6 student.py學生記錄表設計:
# student.py from web import models from django.views import View from django.shortcuts import redirect, render, HttpResponse, reverse from django.forms.models import modelformset_factory from web.forms.studyrecord_form import StudyRecordDeialModelForm # 班級記錄表 class ClassRecordView(View): def get(self, request): # 查找所有的班級學習記錄信息 all_obj = models.ClassStudyRecord.objects.all() # render渲染查找的數據 return render(request, 'student/classstudyrecord.html', {'all_obj': all_obj}) def post(self, request): # 查找批量操作的action,拿到values的值:結果字符串反射方法 action = request.POST.get('action') # 通過getlist獲取所有的select_id的值 selected_id = request.POST.getlist('select_id') # 反射 if hasattr(self, action): ret = getattr(self, action)(selected_id) return self.get(request) # 批量創建學生班級信息記錄 def batch_create(self, selected_id): # 循環這個select_id列表,存在多個班級記錄 for course_record_id in selected_id: # 班級上課記錄對應的班級反向查詢學生對象 all_students = models.ClassStudyRecord.objects.get( pk=course_record_id).class_obj.students.all() # 創建一個列表,存放每個學生的學習記錄 l1 = [] # 循環每個學生記錄,存放在l1中 for student in all_students: obj = models.StudentStudyRecord( student=student, classstudyrecord_id=course_record_id, ) # 通過append添加每個學生的記錄 l1.append(obj) # 批量創建bulk_create models.StudentStudyRecord.objects.bulk_create(l1) # 學生學習記錄信息 class StudyRecordDeialView(View): def get(self, request, class_record_id): # 通過當前學生記錄id找到班級記錄對象 class_record_obj = models.ClassStudyRecord.objects.get(pk=class_record_id) # 找到班級記錄找到對應的所有的學生記錄 all_study_recored = models.StudentStudyRecord.objects.filter( classstudyrecord=class_record_obj, ) # 創建一個modelformset_factory對象(model=學生記錄,form=modelform,extra=默認參數) form_set_obj = modelformset_factory(model=models.StudentStudyRecord, form=StudyRecordDeialModelForm, extra=0) # 通過創建的modelformset_factory創建的對象(queryset=學生記錄對象) formset = form_set_obj(queryset=all_study_recored) return render(request, 'student/study_record_detail.html', {'formset': formset}) def post(self, request, class_record_id): # 創建一個modelformset_factory對象(model=學生記錄,form=modelform,extra=默認參數) form_set_obj = modelformset_factory(model=models.StudentStudyRecord, form=StudyRecordDeialModelForm, extra=0) # 對象中保存request.POST formset = form_set_obj(request.POST) # formset調用is_valid方法 if formset.is_valid(): # 保存formset.save方法 formset.save() else: # 否則打印錯誤信息 print(formset.errors) # 重定向 # return redirect(reverse('study_record', args=(class_record_id,))) return redirect(reverse("class_record"))
3.8 表單設計:
3.8.1 customer_form.py客戶數據表單
# customer_form.py from django import forms from web import models from django.core.exceptions import ValidationError from django import forms # ModelForm字段:客戶表字段 class CustomerModelForm(forms.ModelForm): class Meta: model = models.Customer fields = "__all__" def __init__(self, *args, **kwargs): super(CustomerModelForm, self).__init__(*args, **kwargs) # print(self.fields) from multiselectfield.forms.fields import MultiSelectFormField for field in self.fields: if not isinstance(self.fields[field], MultiSelectFormField): self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.2 enrollment_form.py報名信息表單
# enrollment_form.py from django import forms from web import models from django.core.exceptions import ValidationError # 報名記錄字段 class EnrollmentModelForm(forms.ModelForm): class Meta: model = models.Enrollment fields = "__all__" exclude = ["delete_status", ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.3 record_form.py 跟進記錄表單設計:
# record_form.py from django import forms from web.models import ConsultRecord, Customer from rbac.models import UserInfo # 跟進記錄字段 class ConsultRecordModelForm(forms.ModelForm): class Meta: model = ConsultRecord fields = "__all__" exclude = ["delete_status", ] def __init__(self, request, *args, **kwargs): super().__init__(*args, **kwargs) # 單獨設置字段 # 查詢當前用戶 self.fields["consultant"].queryset = UserInfo.objects.filter(pk=request.user.id) # 當前用戶對應的客戶信息 self.fields["customer"].queryset = Customer.objects.filter(consultant=request.user) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.4 register_form.py 注冊表單設計:
# register_form.py from django import forms from rbac import models from django.core.exceptions import ValidationError # Form字段:注冊用戶表字段 class MyForm(forms.Form): username = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '長度不能小於2!', "required": '該字段不能為空!', "max_length": '字段過長,不能超過32位!' }, label='用戶名', widget=forms.widgets.TextInput(attrs={'placeholder': '用戶名'}) ) password = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '長度不能小於2!', "required": '該字段不能為空!', "max_length": '字段過長,不能超過32位!' }, label='密碼', widget=forms.widgets.PasswordInput(attrs={'placeholder': '密碼'}) ) r_password = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '長度不能小於2!', "required": '該字段不能為空!', "max_length": '字段過長,不能超過32位!' }, label='確認密碼', widget=forms.widgets.PasswordInput(attrs={'placeholder': "密碼"}) ) email = forms.EmailField( error_messages={ "invalid": "請輸入正確的郵箱格式!" }, widget=forms.widgets.EmailInput(attrs={'placeholder': "郵箱"}) ) def clean_username(self): val = self.cleaned_data.get('username') user_obj = models.UserInfo.objects.filter(username=val).first() if user_obj: raise ValidationError('該用戶名已經存在,請換個名字!') else: return val # 全局鈎子 def clean(self): password = self.cleaned_data.get('password') r_password = self.cleaned_data.get('r_password') if password != r_password: self.add_error('r_password', '兩次密碼不一致') # 這是給r_password的錯誤添加這個錯誤信息 # raise ValidationError('兩次密碼不一致!') #被總的錯誤拿到了 else: return self.cleaned_data # 重寫__init__方法: def __init__(self, *args, **kwargs): super(MyForm, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.5 studentrecord_form.py學生學習記錄表單設計:
# syudentrecord_form.py from django import forms from web import models # 學生學習記錄ModeForm class StudyRecordDeialModelForm(forms.ModelForm): class Meta: model = models.StudentStudyRecord # fields = '__all__' fields = ['score', 'homework_note'] def __init__(self, *args, **kwargs): super(StudyRecordDeialModelForm, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.9 web中模板文件匯總:
3.9.1 customer相關:

{% extends "base.html" %} {% block header %} <h1>用戶信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}

{% extends "base.html" %} {# 自定義過濾器 #} {% load rbac %} {% block header %} <h1>公有客戶信息主頁!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查詢操作 #} <form action="{% url 'customer' %}" method="get" class="navbar-form navbar-right"> {# 自定義過濾器,沒有權限不顯示當前操作 #} {% if "/customers/add/"|haspermission:request %} <div class="form-group " style=""> <a href="{% url 'addcustomer' %}" class="btn btn-success pull-right">添加記錄</a> </div> {% endif %} <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ號</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 級聯操作 #} <form action="{% url 'customer' %}" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量刪除</option> <option value="batch_update" name="ii">批量更新客戶狀態</option> <option value="batch_reverse_gs">公戶轉私戶</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已報名</option> <option value="unregistered">未報名</option> <option value="studying">學習中</option> <option value="paid_in_full">學費已交齊</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序號</th> <th>qq</th> <th>姓名</th> <th>性別</th> <th>客戶來源</th> {# <th>轉介紹自學員</th>#} <th>咨詢課程</th> <th>選擇客戶此時的狀態</th> <th>跟進記錄狀態</th> <th>銷售</th> {% if "/customers/edit/1/"|haspermission:request or "/customers/delete/1/"|haspermission:request %} <th>操作</th> {% endif %} </tr> </thead> <tbody> {% for customer in all_customers %} <tr> <td> <input type="checkbox" name="select_id" value="{{ customer.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.name }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.get_source_display }}</td> <td>{{ customer.course }}</td> <td>{{ customer.get_status_display }}</td> <td> <a href="{% url 'my_record' customer.pk %}">詳情</a> </td> <td>{{ customer.consultant.username|default:"暫無" }}</td> {% if "/customers/edit/1/"|haspermission:request or "/customers/delete/1/"|haspermission:request %} <td> {% if "/customers/edit/1/"|haspermission:request %} <a href="{% url 'editcustomer' customer.pk %}" class="btn btn-success btn-xs">編輯</a> {% endif %} {% if "/customers/delete/1/"|haspermission:request %} <a href="{% url 'deletecustomer' customer.pk %}" class="btn btn-danger btn-xs">刪除</a> {% endif %} </td> {% endif %} </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量選擇 #} {% block js %} <script> // select全選 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客戶狀態 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根據索引值獲取標簽內容 console.log(proVal) if (proVal === "批量更新客戶狀態") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}

{% extends "base.html" %} {% block header %} <h1>用戶信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {# <form action="{% url 'editcustomer' 參數pk %}" method="post" novalidate>#} <form action="" method="post" novalidate> {# 這里不指定路徑,默認提交到當前路徑 #} {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}

{% extends "base.html" %} {% block header %} <h1>私有客戶信息主頁!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查詢操作 #} <form action="{% url 'customer' %}" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addmy_customer' %}" class="btn btn-success pull-right">添加記錄</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ號</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 級聯操作 #} <form action="{% url 'my_customer' %}" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量刪除</option> <option value="batch_update" name="ii">批量更新客戶狀態</option> <option value="batch_reverse_gs">私戶轉公戶</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已報名</option> <option value="unregistered">未報名</option> <option value="studying">學習中</option> <option value="paid_in_full">學費已交齊</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序號</th> <th>qq</th> <th>姓名</th> <th>性別</th> <th>客戶來源</th> <th>咨詢課程</th> <th>選擇客戶此時的狀態</th> <th>跟進記錄狀態</th> <th>銷售</th> <th>操作</th> </tr> </thead> <tbody> {% for customer in my_customers %} <tr> <td> <input type="checkbox" name="select_id" value="{{ customer.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.name }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.get_source_display }}</td> {# <td>{{ customer.introduce_from.name }}</td>#} <td>{{ customer.course }}</td> <td>{{ customer.get_status_display }}</td> <td> <a href="{% url 'my_record' customer.pk %}">詳情</a> </td> <td>{{ customer.consultant.username }}</td> <td> <a href="{% url 'editmy_customer' customer.pk %}" class="btn btn-success btn-xs">編輯</a> <a href="{% url 'deletemy_customer' customer.pk %}" class="btn btn-danger btn-xs">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量選擇 #} {% block js %} <script> // select全選 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客戶狀態 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根據索引值獲取標簽內容 console.log(proVal) if (proVal === "批量更新客戶狀態") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.2 enrollment相關:

{% extends "base.html" %} {% block header %} <h1>報名信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}

{% extends "base.html" %} {% block header %} <h1>報名信息表!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查詢操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addenrollment' %}" class="btn btn-success pull-right">添加記錄</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="customer">客戶名稱</option> <option value="status">跟進狀態</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 級聯操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量刪除</option> <option value="batch_update" name="ii">批量更新客戶狀態</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="1">Linux中高級</option> <option value="2">Python高級全棧開發</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序號</th> <th>客戶名稱</th> <th>為什么報名</th> <th>學完想達到的具體期望</th> <th>報名日期</th> <th>所在校區</th> <th>所報班級</th> <th>操作</th> </tr> </thead> <tbody> {% for enrollment in my_enrollments %} <tr> <td> <input type="checkbox" name="select_id" value="{{ enrollment.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ enrollment.customer.name }}</td> <td>{{ enrollment.why_us }}</td> <td>{{ enrollment.your_expectation }}</td> <td>{{ enrollment.enrolled_date|date:"Y-m-d" }}</td> <td>{{ enrollment.school.name }}</td> <td>{{ enrollment.enrolment_class.course }}</td> <td> <a href="{% url 'editenrollment' enrollment.pk %}" class="btn btn-success btn-xs">編輯</a> <a href="{% url 'deleteenrollment' enrollment.pk %}" class="btn btn-danger btn-xs">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量選擇 #} {% block js %} <script> // select全選 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客戶狀態 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根據索引值獲取標簽內容 console.log(proVal); if (proVal === "批量更新客戶狀態") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.3 login_register相關:

{% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Log in</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- iCheck --> <link rel="stylesheet" href="{% static 'adminlte/plugins/iCheck/square/blue.css' %}"> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic"> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <a href="#"><b>CRM</b>login</a> </div> <!-- /.login-logo --> <div class="login-box-body"> <p class="login-box-msg">Sign in to start your session</p> <form novalidate> {% csrf_token %} <div class="form-group has-feedback"> <input type="text" id=username name="username" placeholder="用戶名" class="form-control"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" id=password name="password" placeholder="密碼" class="form-control"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-feedback"> <div class="col-sm-6" style="padding: 0 0;"> <input type="text" id=check-code name="check-code" placeholder="驗證碼" class="form-control"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="col-sm-6"> <img src="{% url 'get_valid_img' %}" alt="" class="captcha"> <span id="error" class="text-danger" style="font-size: 16px;"></span> </div> </div> <div style="height: 40px;"></div> <div class="form-group has-feedback"> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input type="checkbox"> Remember Me </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="button" class="btn btn-primary btn-block btn-flat btn-smt">Sign In</button> </div> <!-- /.col --> </div> </div> </form> {# <div class="social-auth-links text-center">#} {# <p>- OR -</p>#} {# <a href="#" class="btn btn-block btn-social btn-facebook btn-flat"><i class="fa fa-facebook"></i> Sign in#} {# using#} {# Facebook</a>#} {# <a href="#" class="btn btn-block btn-social btn-google btn-flat"><i class="fa fa-google-plus"></i> Sign in#} {# using#} {# Google+</a>#} {# </div>#} <!-- /.social-auth-links --> <a href="#">I forgot my password</a><br> <a href="../../../../../20190606day068Djangomodelform與內置分頁組件/mydjango_register_login/templates/register.html" class="text-center">Register a new membership</a> </div> <!-- /.login-box-body --> </div> <!-- /.login-box --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- iCheck --> <script src="{% static 'adminlte/plugins/iCheck/icheck.min.js' %}"></script> <script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' /* optional */ }); }); $(".btn-smt").click(function () { $.ajax({ url: "{% url 'login' %}", type: "post", data: { "csrfmiddlewaretoken": "{{ csrf_token }}", captcha: $("[name=check-code]").val(), username: $("[name=username]").val(), password: $("[name=password]").val(), }, success: function (data) { console.log(data); if (data.code === 1000) { var href = location.search.slice(6); // 通過切片查詢字符串獲取網址:/home/ if (href) { location.href = href; // 通過查詢字符串獲取網址 } else { location.href = "{% url 'home' %}"; // 直接登錄 } } else { $("#error").text(data.msg); // 驗證碼錯誤 } } }) }); // 點擊更新驗證碼 $(".captcha").click(function () { var url = {% url 'get_valid_img' %}+"?s=" + (new Date()).getTime(); $(".captcha").attr("src", url); }) </script> </body> </html>

{% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Registration Page</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- iCheck --> <link rel="stylesheet" href="{% static 'adminlte/plugins/iCheck/square/blue.css' %}"> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic"> </head> <body class="hold-transition register-page"> <div class="register-box"> <div class="register-logo"> <a href="#"><b>CRM</b>enroll</a> </div> <div class="register-box-body"> <p class="login-box-msg">Register a new membership</p> <form action="{% url 'register' %}" method="post" novalidate> {% csrf_token %} <div class="form-group has-feedback"> {{ form_obj.username }} <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.email }} <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.password }} <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.r_password }} <span class="glyphicon glyphicon-log-in form-control-feedback"></span> </div> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input type="checkbox"> I agree to the <a href="#">terms</a> </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="submit" class="btn btn-primary btn-block btn-flat">Register</button> </div> <!-- /.col --> </div> </form> {# <div class="social-auth-links text-center">#} {# <p>- OR -</p>#} {# <a href="#" class="btn btn-block btn-social btn-facebook btn-flat"><i class="fa fa-facebook"></i> Sign up#} {# using#} {# Facebook</a>#} {# <a href="#" class="btn btn-block btn-social btn-google btn-flat"><i class="fa fa-google-plus"></i> Sign up#} {# using#} {# Google+</a>#} {# </div>#} <a href="../../../../../20190606day068Djangomodelform與內置分頁組件/mydjango_register_login/templates/login.html" class="text-center">I already have a membership</a> </div> <!-- /.form-box --> </div> <!-- /.register-box --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- iCheck --> <script src="{% static 'adminlte/plugins/iCheck/icheck.min.js' %}"></script> <script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' /* optional */ }); }); </script> </body> </html>
3.9.4 record相關:

{% extends "base.html" %} {% block header %} <h1>用戶信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="{% url 'addrecord' %}" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}

{% extends "base.html" %} {% block header %} <h1>跟進信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}

{% extends "base.html" %} {% block header %} <h1>客戶信息跟進表!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查詢操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addrecord' %}" class="btn btn-success pull-right">添加記錄</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="customer">咨詢客戶</option> <option value="status">跟進狀態</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 級聯操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量刪除</option> <option value="batch_update" name="ii">批量更新客戶狀態</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="A">近期無報名計划</option> <option value="B">1個月內報名</option> <option value="C">2周內報名</option> <option value="D">1周內報名</option> <option value="E">定金</option> <option value="F">到班</option> <option value="G">全款</option> <option value="H">無效</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序號</th> <th>咨詢客戶</th> <th>跟進內容</th> <th>跟進狀態</th> <th>跟進日期</th> <th>跟進人</th> <th>操作</th> </tr> </thead> <tbody> {% for record in my_records %} <tr> <td> <input type="checkbox" name="select_id" value="{{ record.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ record.customer.name }}</td> <td>{{ record.note }}</td> <td>{{ record.get_status_display }}</td> <td>{{ record.date|date:"Y-m-d" }}</td> <td>{{ record.consultant }}</td> <td> <a href="{% url 'editrecord' record.pk %}" class="btn btn-success btn-xs">編輯</a> <a href="{% url 'deleterecord' record.pk %}" class="btn btn-danger btn-xs">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量選擇 #} {% block js %} <script> // select全選 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客戶狀態 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根據索引值獲取標簽內容 console.log(proVal); if (proVal === "批量更新客戶狀態") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.5 student相關:

{% extends 'base.html' %} {% block header %} <h1>公有客戶信息主頁!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查詢操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="" class="btn btn-success pull-right">添加記錄</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ號</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 級聯操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_create" name="ii">批量創建學生學習記錄</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已報名</option> <option value="unregistered">未報名</option> <option value="studying">學習中</option> <option value="paid_in_full">學費已交齊</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序號</th> <th>班級</th> <th>節次</th> <th>老師</th> <th>本節課程標題</th> <th>學詳</th> <th>操作</th> </tr> </thead> <tbody> {% for record in all_obj %} <tr> <td><input type="checkbox" name="select_id" value="{{ record.pk }}"></td> <td>{{ forloop.counter }}</td> <td>{{ record.class_obj }}</td> <td>{{ record.day_num }}</td> <td>{{ record.teacher }}</td> <td>{{ record.course_title }}</td> <td><a href="{% url 'study_record' record.pk %}">學詳</a></td> <td> <a href="" class="btn btn-success btn-xs">編輯</a> <a href="" class="btn btn-danger btn-xs">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量選擇 #} {% block js %} <script> // select全選 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客戶狀態 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根據索引值獲取標簽內容 console.log(proVal); if (proVal === "批量更新客戶狀態") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}

{% extends 'base.html' %} {# formset版本 #} {% block header %} <h1>學習記錄詳情!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> <form method="post" action=""> {% csrf_token %} {{ formset.management_form }} <!-- 這句話一定要加上,固定的昂 --> <table class="table table-bordered"> <thead> <tr> <th>姓名</th> <th>考勤</th> <th>作業成績</th> <th>作業評語</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {{ form.id }} <td>{{ form.instance.student }}</td> <td>{{ form.instance.get_record_display }} </td> <td>{{ form.score }} </td> <td>{{ form.homework_note }}</td> </tr> {% endfor %} </tbody> </table> <input type="submit" class="btn btn-success" value="保存"> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %}
3.9.6其他:

{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Starter</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose any other skin. Make sure you apply the skin class to the body tag so the changes take effect. --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/skin-blue.min.css' %}"> </head> <!-- BODY TAG OPTIONS: ================= Apply one or more of the following classes to get the desired effect |---------------------------------------------------------| | SKINS | skin-blue | | | skin-black | | | skin-purple | | | skin-yellow | | | skin-red | | | skin-green | |---------------------------------------------------------| |LAYOUT OPTIONS | fixed | | | layout-boxed | | | layout-top-nav | | | sidebar-collapse | | | sidebar-mini | |---------------------------------------------------------| --> <body class="hold-transition skin-blue sidebar-mini"> <div class="wrapper"> <!-- Main Header --> <header class="main-header"> <!-- Logo --> <a href="index2.html" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels --> <span class="logo-mini"><b>A</b>LT</span> <!-- logo for regular state and mobile devices --> <span class="logo-lg"><b>My</b>crm</span> </a> <!-- Header Navbar --> <nav class="navbar navbar-static-top" role="navigation"> <!-- Sidebar toggle button--> <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> <span class="sr-only">Toggle navigation</span> </a> <!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> <!-- Messages: style can be found in dropdown.less--> <li class="dropdown messages-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-envelope-o"></i> <span class="label label-success">4</span> </a> <ul class="dropdown-menu"> <li class="header">You have 4 messages</li> <li> <!-- inner menu: contains the messages --> <ul class="menu"> <li><!-- start message --> <a href="#"> <div class="pull-left"> <!-- User Image --> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <!-- Message title and timestamp --> <h4> Support Team <small><i class="fa fa-clock-o"></i> 5 mins</small> </h4> <!-- The message --> <p>Why not buy a new awesome theme?</p> </a> </li> <!-- end message --> </ul> <!-- /.menu --> </li> <li class="footer"><a href="#">See All Messages</a></li> </ul> </li> <!-- /.messages-menu --> <!-- Notifications Menu --> <li class="dropdown notifications-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-bell-o"></i> <span class="label label-warning">10</span> </a> <ul class="dropdown-menu"> <li class="header">You have 10 notifications</li> <li> <!-- Inner Menu: contains the notifications --> <ul class="menu"> <li><!-- start notification --> <a href="#"> <i class="fa fa-users text-aqua"></i> 5 new members joined today </a> </li> <!-- end notification --> </ul> </li> <li class="footer"><a href="#">View all</a></li> </ul> </li> <!-- Tasks Menu --> <li class="dropdown tasks-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-flag-o"></i> <span class="label label-danger">9</span> </a> <ul class="dropdown-menu"> <li class="header">You have 9 tasks</li> <li> <!-- Inner menu: contains the tasks --> <ul class="menu"> <li><!-- Task item --> <a href="#"> <!-- Task title and progress text --> <h3> Design some buttons <small class="pull-right">20%</small> </h3> <!-- The progress bar --> <div class="progress xs"> <!-- Change the css width attribute to simulate progress --> <div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"> <span class="sr-only">20% Complete</span> </div> </div> </a> </li> <!-- end task item --> </ul> </li> <li class="footer"> <a href="#">View all tasks</a> </li> </ul> </li> <!-- User Account Menu --> <li class="dropdown user user-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <!-- The user image in the navbar--> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="user-image" alt="User Image"> <!-- hidden-xs hides the username on small devices so only the image appears. --> <span class="hidden-xs">{{ request.user.username }}</span> </a> <ul class="dropdown-menu"> <!-- The user image in the menu --> <li class="user-header"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> <p> Alexander Pierce - Web Developer <small>Member since Nov. 2012</small> </p> </li> <!-- Menu Body --> <li class="user-body"> <div class="row"> <div class="col-xs-4 text-center"> <a href="#">Followers</a> </div> <div class="col-xs-4 text-center"> <a href="#">Sales</a> </div> <div class="col-xs-4 text-center"> <a href="#">Friends</a> </div> </div> <!-- /.row --> </li> <!-- Menu Footer--> <li class="user-footer"> <div class="pull-left"> <a href="{% url 'profile' %}" class="btn btn-default btn-flat">Profile</a> </div> <div class="pull-right"> <a href="{% url 'logout' %}" class="btn btn-default btn-flat">注銷</a> </div> </li> </ul> </li> <!-- Control Sidebar Toggle Button --> <li> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a> </li> </ul> </div> </nav> </header> <!-- Left side column. contains the logo and sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel (optional) --> <div class="user-panel"> <div class="pull-left image"> <img src="{% static 'adminlte/dist/img/user3-128x128.jpg' %}" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> <p>Alexander Pierce</p> <!-- Status --> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> <!-- search form (Optional) --> <form action="#" method="get" class="sidebar-form"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> </button> </span> </div> </form> <!-- /.search form --> <!-- Sidebar Menu --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">HEADER</li> <!-- Optionally, you can add icons to the links --> {# <li><a href="{% url 'home' %}"><i class="fa fa-link"></i> <span>我的主頁</span></a></li>#} {# <li class="treeview">#} {# <a href="#"><i class="fa fa-link"></i> <span>客戶</span>#} {# <span class="pull-right-container">#} {# <i class="fa fa-angle-left pull-right"></i>#} {# </span>#} {# </a>#} {# <ul class="treeview-menu">#} {# <li><a href="#">Link in level 2</a></li>#} {# <li class="active"><a href="{% url 'customer' %}"><i class="fa fa-link"></i> <span>公有客戶</span></a></li>#} {# <li><a href="{% url 'my_customer' %}"><i class="fa fa-link"></i> <span>我的客戶</span></a></li>#} {# </ul>#} {# </li>#} {# <li><a href="{% url 'my_record' %}"><i class="fa fa-link"></i> <span>跟進記錄</span></a></li>#} {# <li><a href="{% url 'my_enrollment' %}"><i class="fa fa-link"></i> <span>報名記錄</span></a></li>#} {% load rbac %} {% menu request %} {# <li><a href="{% url 'class_record' %}"><i class="fa fa-link"></i> <span>班級記錄</span></a></li>#} {# <li><a href="{% url 'role_list' %}"><i class="fa fa-link"></i> <span>角色列表</span></a></li>#} {# <li><a href="{% url 'menu_list' %}"><i class="fa fa-link"></i> <span>菜單權限信息列表</span></a></li>#} {# <li><a href="{% url 'distribute_permissions' %}"><i class="fa fa-link"></i> <span>權限分配</span></a></li>#} </ul> <!-- /.sidebar-menu --> </section> <!-- /.sidebar --> </aside> {% block profile %} <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> {% block header %} Page header {% endblock header %} <small>Optional description</small> </h1> <ol class="breadcrumb"> {# <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li>#} {# <li class="active">Here</li>#} {# 面包屑配置 #} {% for item in request.menu_breadcrumb %} {% if forloop.first %} <li><a href="{{ item.url }}"><i class="fa fa-dashboard"></i>{{ item.title }}</a></li> {% elif forloop.last %} <li><span>{{ item.title }}</span></li> {% else %} <li><a href="{{ item.url }}">{{ item.title }}</a></li> {% endif %} {% endfor %} </ol> </section> <!-- Main content --> <section class="content container-fluid"> {% block content %} {% endblock content %} </section> <!-- /.content --> </div> <!-- /.content-wrapper --> {% endblock profile %} <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="pull-right hidden-xs"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2016 <a href="#">Company</a>.</strong> All rights reserved. </footer> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Create the tabs --> <ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <!-- Home tab content --> <div class="tab-pane active" id="control-sidebar-home-tab"> <h3 class="control-sidebar-heading">Recent Activity</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <i class="menu-icon fa fa-birthday-cake bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">Langdon's Birthday</h4> <p>Will be 23 on April 24th</p> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> <h3 class="control-sidebar-heading">Tasks Progress</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <h4 class="control-sidebar-subheading"> Custom Template Design <span class="pull-right-container"> <span class="label label-danger pull-right">70%</span> </span> </h4> <div class="progress progress-xxs"> <div class="progress-bar progress-bar-danger" style="width: 70%"></div> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> </div> <!-- /.tab-pane --> <!-- Stats tab content --> <div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div> <!-- /.tab-pane --> <!-- Settings tab content --> <div class="tab-pane" id="control-sidebar-settings-tab"> <form method="post"> <h3 class="control-sidebar-heading">General Settings</h3> <div class="form-group"> <label class="control-sidebar-subheading"> Report panel usage <input type="checkbox" class="pull-right" checked> </label> <p> Some information about this general settings option </p> </div> <!-- /.form-group --> </form> </div> <!-- /.tab-pane --> </div> </aside> <!-- /.control-sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <div class="control-sidebar-bg"></div> </div> <!-- ./wrapper --> <!-- REQUIRED JS SCRIPTS --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script> <!-- Optionally, you can add Slimscroll and FastClick plugins. Both of these plugins are recommended to enhance the user experience. --> {% block js %} {% endblock %} </body> </html>

{% extends 'base.html' %}

{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> </head> <body> <div class="container-fluid" style="margin-top: 20px;"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h1>無權訪問!</h1> </div> <div class="panel-body"> <a href="{% url 'home' %}">返回首頁</a><br> <a href="{% url 'logout' %}">退出當前用戶</a> </div> </div> </div> </div> </div> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> <script> </script> </body> </html>

{% extends 'base.html' %} {% load static %} {% block profile %} <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> User Profile </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li> <li><a href="#">Examples</a></li> <li class="active">User profile</li> </ol> </section> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-md-3"> <!-- Profile Image --> <div class="box box-primary"> <div class="box-body box-profile"> <img class="profile-user-img img-responsive img-circle" src="{% static 'adminlte/dist/img/user4-128x128.jpg' %}" alt="User profile picture"> <h3 class="profile-username text-center">Nina Mcintire</h3> <p class="text-muted text-center">Software Engineer</p> <ul class="list-group list-group-unbordered"> <li class="list-group-item"> <b>Followers</b> <a class="pull-right">1,322</a> </li> <li class="list-group-item"> <b>Following</b> <a class="pull-right">543</a> </li> <li class="list-group-item"> <b>Friends</b> <a class="pull-right">13,287</a> </li> </ul> <a href="#" class="btn btn-primary btn-block"><b>Follow</b></a> </div> <!-- /.box-body --> </div> <!-- /.box --> <!-- About Me Box --> <div class="box box-primary"> <div class="box-header with-border"> <h3 class="box-title">About Me</h3> </div> <!-- /.box-header --> <div class="box-body"> <strong><i class="fa fa-book margin-r-5"></i> Education</strong> <p class="text-muted"> B.S. in Computer Science from the University of Tennessee at Knoxville </p> <hr> <strong><i class="fa fa-map-marker margin-r-5"></i> Location</strong> <p class="text-muted">Malibu, California</p> <hr> <strong><i class="fa fa-pencil margin-r-5"></i> Skills</strong> <p> <span class="label label-danger">UI Design</span> <span class="label label-success">Coding</span> <span class="label label-info">Javascript</span> <span class="label label-warning">PHP</span> <span class="label label-primary">Node.js</span> </p> <hr> <strong><i class="fa fa-file-text-o margin-r-5"></i> Notes</strong> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam fermentum enim neque.</p> </div> <!-- /.box-body --> </div> <!-- /.box --> </div> <!-- /.col --> <div class="col-md-9"> <div class="nav-tabs-custom"> <ul class="nav nav-tabs"> <li class="active"><a href="#activity" data-toggle="tab">Activity</a></li> <li><a href="#timeline" data-toggle="tab">Timeline</a></li> <li><a href="#settings" data-toggle="tab">Settings</a></li> <li><a href="#Modify" data-toggle="tab">Modify</a></li> </ul> <div class="tab-content"> <div class="active tab-pane" id="activity"> <!-- Post --> <div class="post"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user1-128x128.jpg' %}" alt="user image"> <span class="username"> <a href="#">Jonathan Burke Jr.</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Shared publicly - 7:30 PM today</span> </div> <!-- /.user-block --> <p> Lorem ipsum represents a long-held tradition for designers, typographers and the like. Some people hate it and argue for its demise, but others ignore the hate as they create awesome tools to help create filler text for everyone from bacon lovers to Charlie Sheen fans. </p> <ul class="list-inline"> <li><a href="#" class="link-black text-sm"><i class="fa fa-share margin-r-5"></i> Share</a></li> <li><a href="#" class="link-black text-sm"><i class="fa fa-thumbs-o-up margin-r-5"></i> Like</a> </li> <li class="pull-right"> <a href="#" class="link-black text-sm"><i class="fa fa-comments-o margin-r-5"></i> Comments (5)</a></li> </ul> <input class="form-control input-sm" type="text" placeholder="Type a comment"> </div> <!-- /.post --> <!-- Post --> <div class="post clearfix"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user7-128x128.jpg' %}" alt="User Image"> <span class="username"> <a href="#">Sarah Ross</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Sent you a message - 3 days ago</span> </div> <!-- /.user-block --> <p> Lorem ipsum represents a long-held tradition for designers, typographers and the like. Some people hate it and argue for its demise, but others ignore the hate as they create awesome tools to help create filler text for everyone from bacon lovers to Charlie Sheen fans. </p> <form class="form-horizontal"> <div class="form-group margin-bottom-none"> <div class="col-sm-9"> <input class="form-control input-sm" placeholder="Response"> </div> <div class="col-sm-3"> <button type="submit" class="btn btn-danger pull-right btn-block btn-sm">Send</button> </div> </div> </form> </div> <!-- /.post --> <!-- Post --> <div class="post"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user6-128x128.jpg' %}" alt="User Image"> <span class="username"> <a href="#">Adam Jones</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Posted 5 photos - 5 days ago</span> </div> <!-- /.user-block --> <div class="row margin-bottom"> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo1.png' %}" alt="Photo"> </div> <!-- /.col --> <div class="col-sm-6"> <div class="row"> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo2.png' %}" alt="Photo"> <br> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo3.jpg' %}" alt="Photo"> </div> <!-- /.col --> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo4.jpg' %}" alt="Photo"> <br> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo1.png' %}" alt="Photo"> </div> <!-- /.col --> </div> <!-- /.row --> </div> <!-- /.col --> </div> <!-- /.row --> <ul class="list-inline"> <li><a href="#" class="link-black text-sm"><i class="fa fa-share margin-r-5"></i> Share</a></li> <li><a href="#" class="link-black text-sm"><i class="fa fa-thumbs-o-up margin-r-5"></i> Like</a> </li> <li class="pull-right"> <a href="#" class="link-black text-sm"><i class="fa fa-comments-o margin-r-5"></i> Comments (5)</a></li> </ul> <input class="form-control input-sm" type="text" placeholder="Type a comment"> </div> <!-- /.post --> </div> <!-- /.tab-pane --> <div class="tab-pane" id="timeline"> <!-- The timeline --> <ul class="timeline timeline-inverse"> <!-- timeline time label --> <li class="time-label"> <span class="bg-red"> 10 Feb. 2014 </span> </li> <!-- /.timeline-label --> <!-- timeline item --> <li> <i class="fa fa-envelope bg-blue"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 12:05</span> <h3 class="timeline-header"><a href="#">Support Team</a> sent you an email</h3> <div class="timeline-body"> Etsy doostang zoodles disqus groupon greplin oooj voxy zoodles, weebly ning heekya handango imeem plugg dopplr jibjab, movity jajah plickers sifteo edmodo ifttt zimbra. Babblely odeo kaboodle quora plaxo ideeli hulu weebly balihoo... </div> <div class="timeline-footer"> <a class="btn btn-primary btn-xs">Read more</a> <a class="btn btn-danger btn-xs">Delete</a> </div> </div> </li> <!-- END timeline item --> <!-- timeline item --> <li> <i class="fa fa-user bg-aqua"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 5 mins ago</span> <h3 class="timeline-header no-border"><a href="#">Sarah Young</a> accepted your friend request </h3> </div> </li> <!-- END timeline item --> <!-- timeline item --> <li> <i class="fa fa-comments bg-yellow"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 27 mins ago</span> <h3 class="timeline-header"><a href="#">Jay White</a> commented on your post</h3> <div class="timeline-body"> Take me to your leader! Switzerland is small and neutral! We are more like Germany, ambitious and misunderstood! </div> <div class="timeline-footer"> <a class="btn btn-warning btn-flat btn-xs">View comment</a> </div> </div> </li> <!-- END timeline item --> <!-- timeline time label --> <li class="time-label"> <span class="bg-green"> 3 Jan. 2014 </span> </li> <!-- /.timeline-label --> <!-- timeline item --> <li> <i class="fa fa-camera bg-purple"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 2 days ago</span> <h3 class="timeline-header"><a href="#">Mina Lee</a> uploaded new photos</h3> <div class="timeline-body"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> </div> </div> </li> <!-- END timeline item --> <li> <i class="fa fa-clock-o bg-gray"></i> </li> </ul> </div> <!-- /.tab-pane --> <div class="tab-pane" id="settings"> <form class="form-horizontal"> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="email" class="form-control" id="inputName" placeholder="Name"> </div> </div> <div class="form-group"> <label for="inputEmail" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputName" placeholder="Name"> </div> </div> <div class="form-group"> <label for="inputExperience" class="col-sm-2 control-label">Experience</label> <div class="col-sm-10"> <textarea class="form-control" id="inputExperience" placeholder="Experience"></textarea> </div> </div> <div class="form-group"> <label for="inputSkills" class="col-sm-2 control-label">Skills</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputSkills" placeholder="Skills"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="checkbox"> <label> <input type="checkbox"> I agree to the <a href="#">terms and conditions</a> </label> </div> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-danger">Submit</button> </div> </div> </form> </div> <!-- /.tab-pane --> <!-- /.tab-modify --> <div class="tab-pane" id="Modify"> <div class="form-group"> {# <h3 style="">修改密碼</h3>#} </div> <form class="form-horizontal" action="{% url 'profile' %}" method="post"> {% csrf_token %} <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">舊密碼</label> <div class="col-sm-10"> <input type="password" class="form-control" id="inputName" name="password" placeholder="舊密碼"> </div> </div> <div class="form-group"> <label for="inputEmail" class="col-sm-2 control-label">新密碼</label> <div class="col-sm-10"> <input type="password" class="form-control" name="r1_password" id="inputEmail" placeholder="新密碼"> </div> </div> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">新密碼</label> <div class="col-sm-10"> <input type="password" class="form-control" name="r2_password" id="inputName" placeholder="新密碼"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> </form> </div> <!-- /.tab-modify --> </div> <!-- /.tab-content --> </div> <!-- /.nav-tabs-custom --> </div> <!-- /.col --> </div> <!-- /.row --> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> <footer class="main-footer"> <div class="pull-right hidden-xs"> <b>Version</b> 2.4.0 </div> <strong>Copyright © 2014-2016 <a href="https://adminlte.io">Almsaeed Studio</a>.</strong> All rights reserved. </footer> {% endblock profile %}

{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Starter</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose any other skin. Make sure you apply the skin class to the body tag so the changes take effect. --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/skin-blue.min.css' %}"> </head> <!-- BODY TAG OPTIONS: ================= Apply one or more of the following classes to get the desired effect |---------------------------------------------------------| | SKINS | skin-blue | | | skin-black | | | skin-purple | | | skin-yellow | | | skin-red | | | skin-green | |---------------------------------------------------------| |LAYOUT OPTIONS | fixed | | | layout-boxed | | | layout-top-nav | | | sidebar-collapse | | | sidebar-mini | |---------------------------------------------------------| --> <body class="hold-transition skin-blue sidebar-mini"> <div class="wrapper"> <!-- Main Header --> <header class="main-header"> <!-- Logo --> <a href="index2.html" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels --> <span class="logo-mini"><b>A</b>LT</span> <!-- logo for regular state and mobile devices --> <span class="logo-lg"><b>NB</b>crm</span> </a> <!-- Header Navbar --> <nav class="navbar navbar-static-top" role="navigation"> <!-- Sidebar toggle button--> <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> <span class="sr-only">Toggle navigation</span> </a> <!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> <!-- Messages: style can be found in dropdown.less--> <li class="dropdown messages-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-envelope-o"></i> <span class="label label-success">4</span> </a> <ul class="dropdown-menu"> <li class="header">You have 4 messages</li> <li> <!-- inner menu: contains the messages --> <ul class="menu"> <li><!-- start message --> <a href="#"> <div class="pull-left"> <!-- User Image --> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <!-- Message title and timestamp --> <h4> Support Team <small><i class="fa fa-clock-o"></i> 5 mins</small> </h4> <!-- The message --> <p>Why not buy a new awesome theme?</p> </a> </li> <!-- end message --> </ul> <!-- /.menu --> </li> <li class="footer"><a href="#">See All Messages</a></li> </ul> </li> <!-- /.messages-menu --> <!-- Notifications Menu --> <li class="dropdown notifications-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-bell-o"></i> <span class="label label-warning">10</span> </a> <ul class="dropdown-menu"> <li class="header">You have 10 notifications</li> <li> <!-- Inner Menu: contains the notifications --> <ul class="menu"> <li><!-- start notification --> <a href="#"> <i class="fa fa-users text-aqua"></i> 5 new members joined today </a> </li> <!-- end notification --> </ul> </li> <li class="footer"><a href="#">View all</a></li> </ul> </li> <!-- Tasks Menu --> <li class="dropdown tasks-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-flag-o"></i> <span class="label label-danger">9</span> </a> <ul class="dropdown-menu"> <li class="header">You have 9 tasks</li> <li> <!-- Inner menu: contains the tasks --> <ul class="menu"> <li><!-- Task item --> <a href="#"> <!-- Task title and progress text --> <h3> Design some buttons <small class="pull-right">20%</small> </h3> <!-- The progress bar --> <div class="progress xs"> <!-- Change the css width attribute to simulate progress --> <div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"> <span class="sr-only">20% Complete</span> </div> </div> </a> </li> <!-- end task item --> </ul> </li> <li class="footer"> <a href="#">View all tasks</a> </li> </ul> </li> <!-- User Account Menu --> <li class="dropdown user user-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <!-- The user image in the navbar--> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="user-image" alt="User Image"> <!-- hidden-xs hides the username on small devices so only the image appears. --> <span class="hidden-xs">{{ request.user.username }}</span> </a> <ul class="dropdown-menu"> <!-- The user image in the menu --> <li class="user-header"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> <p> Alexander Pierce - Web Developer <small>Member since Nov. 2012</small> </p> </li> <!-- Menu Body --> <li class="user-body"> <div class="row"> <div class="col-xs-4 text-center"> <a href="#">Followers</a> </div> <div class="col-xs-4 text-center"> <a href="#">Sales</a> </div> <div class="col-xs-4 text-center"> <a href="#">Friends</a> </div> </div> <!-- /.row --> </li> <!-- Menu Footer--> <li class="user-footer"> <div class="pull-left"> <a href="#" class="btn btn-default btn-flat">Profile</a> </div> <div class="pull-right"> <a href="{% url 'logout' %}" class="btn btn-default btn-flat">注銷</a> </div> </li> </ul> </li> <!-- Control Sidebar Toggle Button --> <li> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a> </li> </ul> </div> </nav> </header> <!-- Left side column. contains the logo and sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel (optional) --> <div class="user-panel"> <div class="pull-left image"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> <p>Alexander Pierce</p> <!-- Status --> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> <!-- search form (Optional) --> <form action="#" method="get" class="sidebar-form"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> </button> </span> </div> </form> <!-- /.search form --> <!-- Sidebar Menu --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">HEADER</li> <!-- Optionally, you can add icons to the links --> <li class="active"><a href="#"><i class="fa fa-link"></i> <span>Link</span></a></li> <li><a href="#"><i class="fa fa-link"></i> <span>Another Link</span></a></li> <li class="treeview"> <a href="#"><i class="fa fa-link"></i> <span>Multilevel</span> <span class="pull-right-container"> <i class="fa fa-angle-left pull-right"></i> </span> </a> <ul class="treeview-menu"> <li><a href="#">Link in level 2</a></li> <li><a href="#">Link in level 2</a></li> </ul> </li> </ul> <!-- /.sidebar-menu --> </section> <!-- /.sidebar --> </aside> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> Page Header <small>Optional description</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li> <li class="active">Here</li> </ol> </section> <!-- Main content --> <section class="content container-fluid"> <!-------------------------- | Your Page Content Here | --------------------------> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="pull-right hidden-xs"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2016 <a href="#">Company</a>.</strong> All rights reserved. </footer> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Create the tabs --> <ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <!-- Home tab content --> <div class="tab-pane active" id="control-sidebar-home-tab"> <h3 class="control-sidebar-heading">Recent Activity</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <i class="menu-icon fa fa-birthday-cake bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">Langdon's Birthday</h4> <p>Will be 23 on April 24th</p> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> <h3 class="control-sidebar-heading">Tasks Progress</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <h4 class="control-sidebar-subheading"> Custom Template Design <span class="pull-right-container"> <span class="label label-danger pull-right">70%</span> </span> </h4> <div class="progress progress-xxs"> <div class="progress-bar progress-bar-danger" style="width: 70%"></div> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> </div> <!-- /.tab-pane --> <!-- Stats tab content --> <div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div> <!-- /.tab-pane --> <!-- Settings tab content --> <div class="tab-pane" id="control-sidebar-settings-tab"> <form method="post"> <h3 class="control-sidebar-heading">General Settings</h3> <div class="form-group"> <label class="control-sidebar-subheading"> Report panel usage <input type="checkbox" class="pull-right" checked> </label> <p> Some information about this general settings option </p> </div> <!-- /.form-group --> </form> </div> <!-- /.tab-pane --> </div> </aside> <!-- /.control-sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <div class="control-sidebar-bg"></div> </div> <!-- ./wrapper --> <!-- REQUIRED JS SCRIPTS --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script> <!-- Optionally, you can add Slimscroll and FastClick plugins. Both of these plugins are recommended to enhance the user experience. --> </body> </html>

{% load static %} <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> <title>Document</title> </head> <body> <h1>這是分頁測試頁!</h1> <form action="/test/" method="get"> <select name="condition" id=""> <option value="qq">QQ</option> <option value="name">姓名</option> </select> <input type="text" name="wd"> <input type="submit" value="Search"> </form> <table class="table"> <thead> <tr> <th>QQ</th> <th>名字</th> </tr> </thead> <tbody> {% for data in all_data %} <tr> <td>{{ data.qq }}</td> <td>{{ data.name }}</td> </tr> {% endfor %} </tbody> </table> {% if not all_data %} <h1 class="text-center">沒有查詢記錄</h1> {% endif %} {{ ret_html|safe }} </body> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> </html>