10-day06-项目开发-用户注册设置免费额度
一、用户注册设置免费额度
- 第一步 :调整用户注册的视图,增加设置免费额度
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/account.py
"""
用户账户相关功能 :注册、登录、短信、注销
"""
from django.shortcuts import render, HttpResponse,redirect
from django.http import JsonResponse
from web_app.forms.account import RegisterModelForm,SendSmsForm, LoginSMSForm, LoginForm
from web_app import models
import uuid
import datetime
def register(request):
""" 注册 """
if request.method =='GET':
form = RegisterModelForm()
return render(request, 'register.html', {'form': form})
# POST
# form 数据校验
form = RegisterModelForm(data=request.POST)
# 若form 数据校验成功
if form.is_valid():
# print(form.cleaned_data) # form.cleaned_data 存放校验成功的数据
# 写入数据库也可以使用create方法,但是需要手动剔除不存在的字段
# from web_app import models
# data = form.cleaned_data
# data.pop('code')
# data.pop('confirm_password')
# instance = models.UserInfo.objects.create(**data)
# 验证通过,写入数据库 (数据库密码加密) ,通过save()方法进行保存数据库,还可以将那些models中不存在的字段会自动剔除
# 用户表中创建一条数据
instance = form.save()
# 创建交易记录
policy_object = models.PricePolicy.objects.filter(category=1, title="个人免费版").first()
models.Transaction.objects.create(
# 状态
status=2,
# 订单号
order=str(uuid.uuid4()),
# 用户
user=instance,
# 价格策略
price_policy=policy_object,
# 数量(年)
count=0,
# 实际支付价格
price=0,
# 开始时间
start_datetime=datetime.datetime.now(),
)
return JsonResponse({'status': True, 'data': '/login/'})
return JsonResponse({'status': False, 'error': form.errors}) # 如果校验未通过则返回 Form 校验的错误信息
二、后台母版样式设计
- 第一步 :项目管理路由
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/urls.py
from django.conf.urls import url, include
from web_app.views import account
from web_app.views import home
from web_app.views import project
urlpatterns = [
url(r'^send/sms/$', account.send_sms, name='send_sms'), # 发送SMS
url(r'^login/sms/$', account.login_sms, name='login_sms'), # 短信登录
url(r'^register/$', account.register, name='register'), # 用户注册
url(r'^login/$', account.login, name='login'), # 用户名邮箱验证码登录
url(r'^image/code/$', account.image_code, name='image_code'), # 图片验证码
url(r'^index/$', home.index, name='index'), # 登录首页
url(r'^logout/$', account.logout, name='logout'), # 退出登录
url(r'^project/list/$', project.project_list, name='project_list'), # 项目列表
]
- 第二步 :后台母模板Template模板
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/layout/manage.html
{% load static %}
{% load project %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'plugin/bootstrap/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'plugin/font-awesome/css/font-awesome.min.css' %}">
<link rel="stylesheet" href="{% static 'css/manage.css' %}">
<style>
.navbar-av {
border-radius: 0;
}
.error-msg {
color: red;
position: absolute;
font-size: 13px;
}
</style>
{% block css %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-av">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'project_list' %}">Tracer</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
{# <ul class="nav navbar-nav">#}
{# {% all_project_list request %}#}
{##}
{# {% if request.tracer.project %}#}
{# {% manage_menu_list request %}#}
{# {% endif %}#}
{# </ul>#}
<ul class="nav navbar-nav navbar-right">
<li><a href="#">工作台</a></li>
<li><a href="#">日历</a></li>
<li><a href="#"> <i class="fa fa-bell-o" aria-hidden="true"></i> </a></li>
<li><a href="#"> <i class="fa fa-bookmark" aria-hidden="true"></i> </a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{ request.tracer.user.username }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'index' %}">官 网</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退 出</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}{% endblock %}
<script src="{% static 'js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'plugin/bootstrap/js/bootstrap.min.js' %}"></script>
{% block js %}{% endblock %}
</body>
</html>
- 第三步 :基于BootStrap进行了CSS样式调整
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/static/css/manage.css
body {
margin: 0;
font-weight: 200;
}
@media (min-width: 1500px) {
.container {
width: 1400px;
}
}
.navbar {
border-radius: 0;
margin-bottom: 0;
color: #ffffff;
height: 52px;
}
@media (min-width: 900px) {
.navbar-header {
float: left;
}
}
.navbar-av {
background-color: #499ef3;
border-color: #499ef3;
}
.navbar-av .navbar-brand {
color: #ffffff;
margin: 0 15px;
}
.navbar-av .navbar-brand:hover,
.navbar-av .navbar-brand:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-text {
color: #ffffff;
}
.navbar-av .navbar-nav {
margin: 0 -15px;
}
.navbar-av .navbar-nav > li {
z-index: 1001;
}
.navbar-av .navbar-nav > li > a {
color: #ffffff;
background-color: #499ef3;
padding-left: 13px;
padding-right: 13px;
}
.navbar-av .navbar-nav > li > a:hover,
.navbar-av .navbar-nav > li > a:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-nav > .active > a,
.navbar-av .navbar-nav > .active > a:hover,
.navbar-av .navbar-nav > .active > a:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-nav > .disabled > a,
.navbar-av .navbar-nav > .disabled > a:hover,
.navbar-av .navbar-nav > .disabled > a:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-toggle {
border-color: #499ef3;
}
.navbar-av .navbar-toggle:hover,
.navbar-av .navbar-toggle:focus {
background-color: #4dabff;
}
.navbar-av .navbar-toggle .icon-bar {
background-color: #ffffff;
}
.navbar-av .navbar-collapse,
.navbar-av .navbar-form {
border-color: #e7e7e7;
}
.navbar-av .navbar-nav > .open > a,
.navbar-av .navbar-nav > .open > a:hover,
.navbar-av .navbar-nav > .open > a:focus {
color: #ffffff;
background-color: #4dabff;
}
@media (max-width: 767px) {
.navbar-av .navbar-nav .open .dropdown-menu {
padding: 0;
}
.navbar-av .navbar-nav .open .dropdown-menu > li > a {
color: #ffffff;
background-color: #499ef3;
}
.navbar-av .navbar-nav .open .dropdown-menu > li > a:hover,
.navbar-av .navbar-nav .open .dropdown-menu > li > a:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-nav .open .dropdown-menu > .active > a,
.navbar-av .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar-av .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #ffffff;
background-color: #4dabff;
}
.navbar-av .navbar-nav .open .dropdown-menu > .disabled > a,
.navbar-av .navbar-nav .open .dropdown-menu > .disabled > a:hover,
.navbar-av .navbar-nav .open .dropdown-menu > .disabled > a:focus {
color: #ffffff;
background-color: #4dabff;
}
}
.navbar-av .navbar-link {
color: #ffffff;
}
.navbar-av .navbar-link:hover {
color: #ffffff;
}
.navbar-av .btn-link {
color: #ffffff;
}
.navbar-av .btn-link:hover,
.navbar-av .btn-link:focus {
color: #ffffff;
}
.navbar-av .btn-link[disabled]:hover,
fieldset[disabled] .navbar-av .btn-link:hover,
.navbar-av .btn-link[disabled]:focus,
fieldset[disabled] .navbar-av .btn-link:focus {
color: #ffffff;
}
.navbar-av .navbar-nav > li > .sep{
padding: 15px 5px;display: inline-block
}
.dropdown-menu>li{
padding: 0 5px;
color: black;
}
- 第四步 :编写项目管理页面子模板继承自母模板
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/project_list.html
{% extends 'layout/manage.html' %}
- 第五步 :编写views视图
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/project.py
from django.shortcuts import render, redirect
def project_list(request):
""" 项目列表 """
return render(request, 'project_list.html')
三、添加按钮和权限中间件处理
- 后台 :登录成功之后才可以访问;
- 官网 :都可以访问
- 实现方案 :通过
中间件
+白名单
对后台管理的权限 进行处理
- 第一步 :在settings中定义无需登录就可以访问的白名单路由
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat Bug_manager/settings.py
...
...
######## 登录报名单 :无需登录就可以访问的页面 ########
WHITE_REGEX_URL_LIST = [
"/register/",
"/send/sms/",
"/login/",
"/login/sms/",
"/image/code/",
"/index/",
]
...
...
- 第二步 :调整认证的中间件,增加URL对白名单以外的路径,检查用户是否登录
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from web_app import models
from django.conf import settings
from django.shortcuts import redirect
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
""" 如果用户已经登录,则request中赋值 """
user_id = request.session.get('user_id', 0) # 用户登录则session中有用户ID,如果没有则为0
# 如果登录的用户存在则返回用户对象,不存在返回None
user_object = models.UserInfo.objects.filter(id=user_id).first()
request.bug_manager = user_object
# 白名单:没有登录都可以访问的URL
"""
1. 获取当用户访问的URL
2. 检查URL是否在白名单中,如果再则可以继续向后访问,如果不在则进行判断是否已登录
request.path_info 表示用户访问的URI
"""
if request.path_info in settings.WHITE_REGEX_URL_LIST:
return # 中间件返回None表示成功
# 检查用户是否已登录,已登录继续往后走;未登录则返回登录页面。
if not request.bug_manager.user:
return redirect('login')
四、中间件对的登录的用户额度获取及用户信息的封装
4.1、方式一 :免费额度在交易记录中存储
- 在登录成功后,获取当前用户拥有的价格策略【额度】,也使用 request 自定义字段存放;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from web_app import models
from django.conf import settings
from django.shortcuts import redirect
import datetime
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
""" 如果用户已经登录,则request中赋值 """
user_id = request.session.get('user_id', 0) # 用户登录则session中有用户ID,如果没有则为0
# 如果登录的用户存在则返回用户对象,不存在返回None
user_object = models.UserInfo.objects.filter(id=user_id).first()
request.bug_manager = user_object
# 白名单:没有登录都可以访问的URL
"""
1. 获取当用户访问的URL
2. 检查URL是否在白名单中,如果再则可以继续向后访问,如果不在则进行判断是否已登录
request.path_info 表示用户访问的URI
"""
if request.path_info in settings.WHITE_REGEX_URL_LIST:
return # 中间件返回None表示成功
# 检查用户是否已登录,已登录继续往后走;未登录则返回登录页面。
if not request.bug_manager:
return redirect('login')
# 登录成功之后,访问后台管理时:获取当前用户所拥有的额度(交易记录表)
# 方式一:免费额度在交易记录中存储
# 获取当前用户ID值最大(最近交易记录)
_object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first()
# 判断是否已过期
current_datetime = datetime.datetime.now()
if _object.end_datetime and _object.end_datetime < current_datetime:
_object = models.Transaction.objects.filter(user=user_object, status=2, price_policy__category=1).first()
# request.transaction = _object # (存储了整张交易记录表中的信息)
request.price_policy = _object.price_policy # (存储了登录的用户及用户的额度信息)
4.2、方式二 :免费的额度存储配置文件
...
...
# 方式二:免费的额度存储配置文件
"""
# 获取当前用户ID值最大(最近交易记录)
_object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first()
if not _object:
# 没有购买
request.price_policy = models.PricePolicy.objects.filter(category=1, title="个人免费版").first()
else:
# 付费版
current_datetime = datetime.datetime.now()
if _object.end_datetime and _object.end_datetime < current_datetime:
request.price_policy = models.PricePolicy.objects.filter(category=1, title="个人免费版").first()
else:
request.price_policy = _object.price_policy
"""
...
...
4.3、优化
- 第一步 :上面中间件对的登录的用户获取额度,可以以如下方式在视图中使用
from django.shortcuts import render, redirect
def project_list(request):
""" 项目列表 """
request.bug_manager
request.price_polcy
return render(request, 'project_list.html')
- 第二步 :如果觉得上面获取方式不好,可以再次对中间件进行优化;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/middleware/auth.py
from django.utils.deprecation import MiddlewareMixin
from web_app import models
from django.conf import settings
from django.shortcuts import redirect
import datetime
class Bug_manager(object):
def __init__(self):
self.user = None
self.price_policy = None
self.project = None
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
""" 如果用户已经登录,则request中赋值 """
request.bug_manager = Bug_manager()
user_id = request.session.get('user_id', 0) # 用户登录则session中有用户ID,如果没有则为0
# 如果登录的用户存在则返回用户对象,不存在返回None
user_object = models.UserInfo.objects.filter(id=user_id).first()
request.bug_manager.user = user_object
# 白名单:没有登录都可以访问的URL
"""
1. 获取当用户访问的URL
2. 检查URL是否在白名单中,如果再则可以继续向后访问,如果不在则进行判断是否已登录
request.path_info 表示用户访问的URI
"""
if request.path_info in settings.WHITE_REGEX_URL_LIST:
return # 中间件返回None表示成功
# 检查用户是否已登录,已登录继续往后走;未登录则返回登录页面。
if not request.bug_manager.user:
return redirect('login')
# 登录成功之后,访问后台管理时:获取当前用户所拥有的额度(交易记录表)
# 方式一:免费额度在交易记录中存储
# 获取当前用户ID值最大(最近交易记录)
_object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first()
# 判断是否已过期
current_datetime = datetime.datetime.now()
if _object.end_datetime and _object.end_datetime < current_datetime:
_object = models.Transaction.objects.filter(user=user_object, status=2, price_policy__category=1).first()
# request.transaction = _object # (存储了整张交易记录表中的信息)
request.bug_manager.price_policy = _object.price_policy # (存储了登录的用户及用户的额度信息)
- 第三步 :优化后的中间件对的登录的用户获取额度,可以以如下方式在视图中使用;
from django.shortcuts import render, redirect
def project_list(request):
""" 项目列表 """
request.bug_manager.user
request.bug_manager.price_policy
return render(request, 'project_list.html')
- 第四步 :善后调整,封装完成后,回想此前首页模板对用户是否登录的判断也需要调整;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/layout/basic.html
...
...
<ul class="nav navbar-nav navbar-right">
{% if request.bug_manager.user %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{ request.bug_manager.user.username }} <span class="caret"></span></a>
<ul class="dropdown-menu">
{# <li><a href="#">Action</a></li>#}
<li><a href="#">管理中心</a></li>
{# <li><a href="#">Something else here</a></li>#}
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退 出</a></li>
</ul>
</li>
{% else %}
<li><a href="{% url 'login' %}">登 录</a></li>
<li><a href="{% url 'register' %}">注 册</a></li>
{% endif %}
</ul>
...
...
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/layout/manage.html
...
...
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{ request.bug_manager.user.username }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'index' %}">官 网</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退 出</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}{% endblock %}
...
...
五、创建项目
-
BootSrtap 模态框 :https://v3.bootcss.com/javascript/#modals
-
需要注意的是 :模态对话框提交数据,一定要使用Ajax;
-
第一步 :使用Form,生成创建项目相关字段
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/project.py
# -*- coding:utf-8 -*-
from django import forms
from django.core.exceptions import ValidationError
from web_app.forms.bootstrap import BootStrapForm
from web_app import models
class ProjectModelForm(BootStrapForm, forms.ModelForm):
bootstrap_class_exclude = ['color']
class Meta:
model = models.Project
fields = ['name', 'color', 'desc']
- 第二步 :定义Views视图,将form传入前端
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/project.py
from django.shortcuts import render, redirect
from web_app.forms.project import ProjectModelForm
def project_list(request):
""" 项目列表 """
if request.method == 'GET':
form = ProjectModelForm()
return render(request, 'project_list.html', {'form': form})
- 第三步 :编写Template模板,遍历form,生成模态框中的字段
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/project_list.html
{% extends 'layout/manage.html' %}
{% block css %}
<style>
.project {
margin-top: 10px;
}
.panel-body {
padding: 0;
display: flex;
flex-direction: row;
justify-content: left;
align-items: flex-start;
flex-wrap: wrap;
}
.panel-body > .item {
border-radius: 6px;
width: 228px;
border: 1px solid #dddddd;
margin: 20px 10px;
}
.panel-body > .item:hover {
border: 1px solid #f0ad4e;
}
.panel-body > .item > .title {
height: 104px;
color: white;
display: flex;
justify-content: center;
align-items: center;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
font-size: 15px;
text-decoration: none;
}
.panel-body > .item > .info {
padding: 10px 10px;
display: flex;
justify-content: space-between;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
color: #8c8c8c;
}
.panel-body > .item > .info a {
text-decoration: none;
}
.panel-body > .item > .info .fa-star {
font-size: 18px;
}
.color-radio label {
margin-left: 0;
padding-left: 0;
}
.color-radio input[type="radio"] {
display: none;
}
.color-radio input[type="radio"] + .cycle {
display: inline-block;
height: 25px;
width: 25px;
border-radius: 50%;
border: 2px solid #dddddd;
}
.color-radio input[type="radio"]:checked + .cycle {
border: 2px solid black;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid project">
<div style="margin: 10px 0;">
<a class="btn btn-primary" data-toggle="modal" data-target="#addModal">
<i class="fa fa-plus-circle" aria-hidden="true"></i> 新建项目
</a>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">新建项目</h4>
</div>
<div class="modal-body">
<form id="addForm">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="error-msg"></span>
</div>
{% endfor %}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取 消</button>
<button id="btnSubmit" type="button" class="btn btn-primary">确 定</button>
</div>
</div>
</div>
</div>
{% endblock %}
- 可以发现由于描述字段设置的数据库字段类型为
CharField
,所以需要在Form中对此字段进行部件调整;
- 第四步 :对描述字段进行部件调整
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/project.py
# -*- coding:utf-8 -*-
from django import forms
from django.core.exceptions import ValidationError
from web_app.forms.bootstrap import BootStrapForm
from web_app import models
class ProjectModelForm(BootStrapForm, forms.ModelForm):
# desc = forms.CharField(widget=forms.Textarea(attrs={'xx': 123}))
class Meta:
model = models.Project
fields = ['name', 'color', 'desc']
# # widgets 重写字段类型
widgets = {
'desc': forms.Textarea,
}
- 第五步 :Template模板,编写Ajax请求
模态框,必须使用Ajax发送请求
,为确定的IDbtnSubmit
,绑定一个Ajax POST
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/project_list.html
{% extends 'layout/manage.html' %}
{% block css %}
<style>
.project {
margin-top: 10px;
}
.panel-body {
padding: 0;
display: flex;
flex-direction: row;
justify-content: left;
align-items: flex-start;
flex-wrap: wrap;
}
.panel-body > .item {
border-radius: 6px;
width: 228px;
border: 1px solid #dddddd;
margin: 20px 10px;
}
.panel-body > .item:hover {
border: 1px solid #f0ad4e;
}
.panel-body > .item > .title {
height: 104px;
color: white;
display: flex;
justify-content: center;
align-items: center;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
font-size: 15px;
text-decoration: none;
}
.panel-body > .item > .info {
padding: 10px 10px;
display: flex;
justify-content: space-between;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
color: #8c8c8c;
}
.panel-body > .item > .info a {
text-decoration: none;
}
.panel-body > .item > .info .fa-star {
font-size: 18px;
}
.color-radio label {
margin-left: 0;
padding-left: 0;
}
.color-radio input[type="radio"] {
display: none;
}
.color-radio input[type="radio"] + .cycle {
display: inline-block;
height: 25px;
width: 25px;
border-radius: 50%;
border: 2px solid #dddddd;
}
.color-radio input[type="radio"]:checked + .cycle {
border: 2px solid black;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid project">
<div style="margin: 10px 0;">
<a class="btn btn-primary" data-toggle="modal" data-target="#addModal">
<i class="fa fa-plus-circle" aria-hidden="true"></i> 新建项目
</a>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">新建项目</h4>
</div>
<div class="modal-body">
<form id="addForm">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="error-msg"></span>
</div>
{% endfor %}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取 消</button>
<button id="btnSubmit" type="button" class="btn btn-primary">确 定</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(function () {
bindSubmit();
});
function bindSubmit() {
$('#btnSubmit').click(function () {
$.ajax({
url: "{% url 'project_list' %}",
type: "POST",
data: $('#addForm').serialize(),
dataType: "JSON",
success: function (res) {
console.log(res);
if (res.status) {
location.href = location.href; // 页面主动刷新,同 location.reload()
// location.reload()
} else {
$.each(res.error, function (key, value) {
$("#id_" + key).next().text(value[0]);
})
}
}
})
})
}
</script>
{% endblock %}
- 第六步 :Form校验,
校验项目名是否存在
及当前用户是否还有额度创建项目
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/project.py
# -*- coding:utf-8 -*-
from django import forms
from django.core.exceptions import ValidationError
from web_app.forms.bootstrap import BootStrapForm
from web_app import models
class ProjectModelForm(BootStrapForm, forms.ModelForm):
# 重写__init__ 仅为在views视图调用Form初始化函数式多传递request参数
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
# desc = forms.CharField(widget=forms.Textarea(attrs={'xx': 123}))
class Meta:
model = models.Project
fields = ['name', 'color', 'desc']
# # widgets 重写字段类型
widgets = {
'desc': forms.Textarea,
}
def clean_name(self):
""" 项目校验 """
# 1、当前用户是否已创建过此项目
name = self.cleaned_data['name']
exists = models.Project.objects.filter(name=name, creator=self.request.bug_manager.user).exists()
print(exists)
if exists:
raise ValidationError("项目名称已存在")
# 2、当前用户是否还有额度进行创建项目
# 最多创建N个项目
# self.request.tracer.price_policy.project_num
# 现在已创建多少项目?
count = models.Project.objects.filter(creator=self.request.bug_manager.user).count()
if count >= self.request.bug_manager.price_policy.project_num:
raise ValidationError('项目个数超限,请购买套餐')
return name
- 第七步 :Views视图中接收到Ajax POST数据,传递给Form进行表单验证
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/project.py
from django.shortcuts import render, redirect
from web_app.forms.project import ProjectModelForm
from django.http import JsonResponse
def project_list(request):
""" 项目列表 """
if request.method == 'GET':
form = ProjectModelForm(request=request)
return render(request, 'project_list.html', {'form': form})
# POST
form = ProjectModelForm(data=request.POST, request=request)
if form.is_valid():
# 通过验证,创建项目 :用户仅填写了项目名称、颜色、描述 ,查看设计的项目表,只有创建者没有默认值,所以获取创建人再写入数据库,不然会报错
form.instance.creator = request.bug_manager.user # 获取创建者
form.save()
return JsonResponse({'status': True})
return JsonResponse({'status': False, 'error': form.errors})
- 第八步 :创建项目测试,免费版默认为最多创建三个
向往的地方很远,喜欢的东西很贵,这就是我努力的目标。