号码管理平台-Django
号码管理系统
1.表结构的设计
一共有三个数据表, 一个号码表, 一个 管理员表,一个部门表, 其中 号码表的 admin 字段和 管理员表存在外键关系, 管理员表中的 depart_id 和 部门表存在外键关系。
2. 创建项目
使用 pycharm 创建一个django 项目,
在终端使用 python manage.py startapp app名称, 创建app, 这里 为了方便使用 sqllite数据库
3. 创建相关的表结构
# app/models.py
from django.db import models
# Create your models here.
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name="标题", max_length=16)
class Admin(models.Model):
""" 管理员表 """
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=32)
# 增加一个年龄字段, 创建管理员的时候,不填的话, 默认为空
age = models.IntegerField(verbose_name="年龄", null=True, blank=True)
gender = models.CharField(verbose_name="性别",
choices=[(1, "男"), (2, "女")])
depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)
class Phone(models.Model):
""" 号码表 """
mobile = models.CharField(verbose_name="手机号", max_length=11)
price = models.PositiveIntegerField(verbose_name="价格", default=0)
level = models.SmallIntegerField(
verbose_name="级别",
choices=[
(1, "1级"),
(2, "2级"),
(3, "3级"),
(4, "4级"),
],
default=1
)
status_choices = [
(1, "已使用"),
(2, "未使用"),
]
status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
admin = models.ForeignKey(verbose_name="管理员", to="Admin", on_delete=models.CASCADE)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
在项目终端, 使用 python manage.py makemigrations 和 python manage.py migrate 将相关的类模型 映射到数据库
4. 用户登录
- 基本逻辑
- 密码 + 密文存储
- 图片验证码
app 下 创建 static 文件夹及其子文件夹,不同类型的代码放到不同的位置
登录前端页面实现
<!--login.html-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static "plugins/jquery/jquery.3.6.min.js" %}">
<link rel="stylesheet" href="{% static "plugins/bootstrap-5.3.6-dist/css/bootstrap.min.css" %}">
<script src="{% static "plugins/bootstrap-5.3.6-dist/js/bootstrap.min.js" %}"></script>
<style>
.login-box{
width:400px;
height: 280px;
margin-left:auto;
margin-right:auto;
border:1px solid #dddddd;
margin-top:150px;
padding-left: 20px;
padding-right: 20px;
}
.login-box h2{
text-align: center;
}
</style>
</head>
<body>
<div class="login-box">
<h2>用户登录</h2>
<form method="post" action="/login/" novalidate>
{% csrf_token %}
<div class="form-group">
<label>{{ form.username.label }}</label>
{{ form.username }}
<span style="color: red;">{{ form.username.errors.0 }}</span>
</div>
<div class="form-group">
<label>{{ form.password.label }}</label>
{{ form.password }}
<span style="color:red;">{{ form.password.errors.0 }}</span>
</div>
<div class="form-group">
<label>{{ form.code.label }}</label>
<div class="row">
<div class="col-sm-6">
{{ form.code }}
</div>
<div class="col-sm-6">
<img src="xxx" alt="">
</div>
</div>
<span style="color:red;">{{ form.code.errors.0 }}</span>
</div>
<button type="submit" class="btn btn-primary">提交</button>
<span style="color:red;">{{ error }}</span>
</form>
</div>
</body>
</html>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
# views.py
from django.shortcuts import render
from django import forms
from django.core.validators import RegexValidator
# Create your views here.
class LoginForm(forms.Form):
username = forms.CharField(
label="用户名",
widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入用户名"}),
# validators=[RegexValidator(r'\w{6,}$', '用户名格式错误')]
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(attrs={"class":"form-control", "placeholder":"输入密码"}),
)
code = forms.CharField(
label="验证码",
widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入验证码"}),
)
def login(request):
""" 用户登录"""
form = LoginForm()
return render(request, "login.html", {"form": form})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
5.展示图片验证码
在 项目目录下创建一个 utils 文件夹, 在下面创建一个 check_code.py 用来存放生成图片验证码的脚本,
# 生成图片验证码的脚本, 返回一个 图片对象和一个 图片中文本的字符串, 其中使用到了 pillow 模块
# pip install pillow
from PIL import Image, ImageDraw, ImageFont
import string
import random
class CaptchaGenerator:
def __init__(self, width=100, height=30, font_size=20):
"""
初始化验证码生成器。
Args:
- width (int): 图片宽度,默认为250像素。
- height (int): 图片高度,默认为60像素。
- font_size (int): 字体大小,默认为48像素。
"""
self.width = width
self.height = height
self.font_size = font_size
self.chars = string.ascii_letters + string.digits # 验证码字符集合
self.bgcolor = (255, 255, 255) # 图片背景颜色
self.linecolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128)) # 干扰线颜色
self.dotcolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128)) # 干扰点颜色
self.fontcolor = (random.randint(0, 128), random.randint(0, 128), random.randint(0, 128)) # 字体颜色
def generate_captcha(self):
"""
生成验证码图片和文本。
Returns:
- image (PIL.Image.Image): 生成的验证码图片对象。
- captcha_text (str): 生成的验证码文本。
"""
captcha_text = ''.join(random.choice(self.chars) for _ in range(4)) # 随机生成验证码文本
image = Image.new('RGB', (self.width, self.height), self.bgcolor) # 创建RGB模式的空白图片
draw = ImageDraw.Draw(image) # 创建可在图像上绘图的对象
font = ImageFont.load_default().font_variant(size=self.font_size) # 加载默认字体并调整大小
# 绘制干扰线
for i in range(5):
x1 = random.randint(0, self.width)
y1 = random.randint(0, self.height)
x2 = random.randint(0, self.width)
y2 = random.randint(0, self.height)
draw.line((x1, y1, x2, y2), fill=self.linecolor, width=3)
# 绘制干扰点
for i in range(200):
x = random.randint(0, self.width)
y = random.randint(0, self.height)
draw.point((x, y), fill=self.dotcolor)
# 绘制验证码文字,包括阴影效果
for i, char in enumerate(captcha_text):
shadow_offset = random.randint(0, 3)
shadow_color = (0, 0, 0)
draw.text((20 + i * 20 + shadow_offset, 5 + shadow_offset), char, font=font, fill=shadow_color) # 绘制阴影效果的文字
draw.text((20 + i * 20, 5), char, font=font, fill=self.fontcolor) # 绘制文字
return image, captcha_text
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
修改 登录前端代码 图片验证码的位置 img标签的 src 为 "/img/code/"
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static "plugins/jquery/jquery.3.6.min.js" %}">
<link rel="stylesheet" href="{% static "plugins/bootstrap-5.3.6-dist/css/bootstrap.min.css" %}">
<script src="{% static "plugins/bootstrap-5.3.6-dist/js/bootstrap.min.js" %}"></script>
<style>
.login-box{
width:400px;
height: 280px;
margin-left:auto;
margin-right:auto;
border:1px solid #dddddd;
margin-top:150px;
padding-left: 20px;
padding-right: 20px;
}
.login-box h2{
text-align: center;
}
</style>
</head>
<body>
<div class="login-box">
<h2>用户登录</h2>
<form method="post" action="/login/" novalidate>
{% csrf_token %}
<div class="form-group">
<label>{{ form.username.label }}</label>
{{ form.username }}
<span style="color: red;">{{ form.username.errors.0 }}</span>
</div>
<div class="form-group">
<label>{{ form.password.label }}</label>
{{ form.password }}
<span style="color:red;">{{ form.password.errors.0 }}</span>
</div>
<div class="form-group">
<label>{{ form.code.label }}</label>
<div class="row">
<div class="col-sm-6">
{{ form.code }}
</div>
<div class="col-sm-6">
<!--修改了下面这里-->
<img src="/img/code/" alt="">
</div>
</div>
<span style="color:red;">{{ form.code.errors.0 }}</span>
</div>
<button type="submit" class="btn btn-primary">提交</button>
<span style="color:red;">{{ error }}</span>
</form>
</div>
</body>
</html>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
urls.py 中 书写 一个视图函数和url 的映射关系
views.py 中写 生成验证码图片,并返回的 视图函数
from django.shortcuts import render, HttpResponse
from utils.check_code import CaptchaGenerator
from io import BytesIO
def img_code(request):
# 生成图片
obj = CaptchaGenerator()
image_obj, code_str = obj.generate_captcha()
# 图片内容写入内存, 从内存中读取并返回
print(code_str)
stream = BytesIO()
image_obj.save(stream,'png')
return HttpResponse(stream.getvalue())
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
效果图片
6.点击更换图片
<div class="col-sm-6">
<img src="/img/code/" id="codeImage">
</div>
<!--验证码 img标签设置一个 id-->
<!--利用 jquery id 选择器 获取这个标签, 给这个标签绑定一个点击事件,首先要引入jquery-->
<script src="{% static "plugins/jquery/jquery.3.6.min.js" %}"></script>
<script>
$(function(){
$("#codeImage").click(function(){
// 获取img 标签的 src 属性;
var url = $(this).attr('src');
// 设置属性值不同于之前的,相当于向 url 发送了新的请求, 获取了新的图片验证码
$(this).attr('src', url + "?");
});
})
</script>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
效果, 点击图片, 会更换图片验证码
7.使用session 保存验证码
更改 获取图片验证码的视图函数,将验证码内容写入到 session 中,方便后面登录时验证码的校验。
def img_code(request):
# 1.生成图片
obj = CaptchaGenerator()
image_obj, code_str = obj.generate_captcha()
# 2.图片内容写入内存, 从内存中读取并返回
print(code_str)
stream = BytesIO()
image_obj.save(stream,'png')
# 3.图片的内容写入session 中, 设置 60s 超时
request.session['image_code'] = code_str
request.session.set_expiry(60)
return HttpResponse(stream.getvalue())
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
8.用户登录逻辑校验
用户密码存储在数据库中不能使用明文,需要加密, 可以使用 hashlib 的 md5 加密存储到数据库,
进行登录校验的时候,也需要将 密码进行 md5 加密和 存储在数据库中的加密密码进行对比。
在 utils 文件夹下 创建一个 encrypt.py 文件,在其中写 md5 加密的函数
加密的 key 使用 django settings.py 中的 SECRET_KEY
# views.py
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from django.core.validators import RegexValidator
from utils.check_code import CaptchaGenerator
from io import BytesIO
from number_management import models
from utils.encrypt import md5
# Create your views here.
class LoginForm(forms.Form):
username = forms.CharField(
label="用户名",
widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入用户名"}),
# validators=[RegexValidator(r'\w{6,}$', '用户名格式错误')]
)
password = forms.CharField(
label="密码",
# 设置 render_value=True , 输入验证码错误的时候,保留之前输入的密码
widget=forms.PasswordInput(attrs={"class":"form-control", "placeholder":"输入密码"}, render_value=True),
)
code = forms.CharField(
label="验证码",
widget=forms.TextInput(attrs={"class":"form-control", "placeholder":"输入验证码"}),
)
def login(request):
""" 用户登录"""
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# 判断验证码是否正确
image_code = request.session.get("image_code")
if not image_code:
form.add_error("code", "验证码已过期")
return render(request, "login.html", {"form": form})
if image_code.upper() != form.cleaned_data['code'].upper():
form.add_error("code", "验证码错误")
return render(request, "login.html", {"form": form})
# 验证码正确, 去数据库校验用户名和密码
user = form.cleaned_data['username']
# 密码不能够存储明文的, 可以使用 hashlib 中 的 md5 进行加密
pwd = form.cleaned_data['password']
encrypt_password = md5(pwd)
print(user, encrypt_password)
admin_object=models.Admin.objects.filter(username=user,password=encrypt_password).first()
if not admin_object:
return render(request, "login.html", {"form": form, "error":"用户名或密码错误"})
request.session['info'] = {"id": admin_object.id, "name": admin_object.username}
request.session.set_expiry(60 * 60 * 24 * 7)
return redirect("/home")
def img_code(request):
# 1.生成图片
obj = CaptchaGenerator()
image_obj, code_str = obj.generate_captcha()
# 2.图片内容写入内存, 从内存中读取并返回
print(code_str)
stream = BytesIO()
image_obj.save(stream,'png')
# 3.图片的内容写入session 中, 设置 60s 超时
request.session['image_code'] = code_str
request.session.set_expiry(60)
return HttpResponse(stream.getvalue())
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
6.基于中间件进行用户校验
在项目的 utils 文件夹中写一个middleware.py 写自定义的中间件,用来做用户校验
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
# 1. 不用登录就可以访问处理
if request.path_info in ["/login/", "/img/code/"]:
# return 为空代码继续向后走
return
# 2. 获取 session
info_dict = request.session.get("info")
# 未登录, 重定向到 login 页面
if not info_dict:
return redirect("login")
# 已登录, 将登录信息储存到 request 中
request.info_dict = info_dict
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
7.Home页面展示
写一个layout.html 作为母版,母版中写一些公共的内容,例如导航条,
导航条相关的代码从 bootstrp 官方文档中获取,修改成自己需要的样子。
注意 bootstrap依赖于 jquery , 所以也需要引入 jquery,
使用 block 进行占位
<!--layout.html-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.css' %}">
{% block css %}
{% endblock %}
</head>
<body>
<nav class="navbar navbar-default" style="border-radius:0;">
<div class="container">
<!-- 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="#">号码管理系统</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">
<li><a href="#">用户管理</a></li>
<li><a href="#">号码管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.info_dict.name }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">修改密码</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container">
{% block content %}
{% endblock %}
</div>
<script src="{% static 'plugins/jquery/jquery.3.6.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>
{% block js %}
{% endblock %}
</body>
</html>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
home 页面继承 layout.html (模版继承)
8.注销
修改 layout.html 中的 注销 a 标签的 href 属性,点击 a 标签向 退出登录视图函数发送请求。
在 app 中的 views.py 中写一个 退出登录的视图函数
urls.py 中的 路由和视图函数的映射
from django.urls import path
from number_management import views
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", views.login, name="login"),
path("logout/", views.logout, name="logout"),
path("img/code/", views.img_code, name="img_code"),
path("home", views.home, name="home")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
代码分类, 使结构变的清晰一些。
在 相关的 app 下创建一个 views 文件夹, 根据功能的不同,将不同的 视图函数放到不同的脚本中
导入模块时,为了使条理更加的清晰, 一般把内置模块放到 脚本最上面,第三方模块,短的在上面, 长的在下面
然后 自定义模块
urls.py 中的视图函数的导入位置也要做响应的修改
from django.urls import path
from number_management.views import account
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", account.login, name="login"),
path("logout/", account.logout, name="logout"),
path("img/code/", account.img_code, name="img_code"),
path("home", account.home, name="home")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
9.用户列表
修改 用户管理a 标签的 href 属性,点击 a 标签会向相关的视图函数发送请求,跳转页面
在 app 下的 views文件夹下创建一个 admin.py 用来存放 号码管理员相关的视图函数
# app/views/admin.py
from django.shortcuts import render
from number_management import models
def admin_list(request):
"""
用户列表
:param request:
:return:
"""
# [obj, obj, obj] order_by("-id") 根据id 倒序排序
queryset = models.Admin.objects.all().order_by("-id")
# for row in queryset:
# 如果 models.py 该字段中定义了 choice 对应关系, 使用对象.get_字段名_display() 获取 choice 中设置的
# 对应的值 例如 row.get_gender_dispaly 可以获取 男或者 女
# choices=[(1, "男"), (2, "女")]
# row.depart.title , 跨表查询
# print(row.username, row.password, row.gender, row.get_gender_display(),row.depart.title)
# mpx 8fe4282d77a078f5a064fff057288ee9 1 男 IT
return render(request,"admin_list.html", {"queryset": queryset})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
将相关的管理员信息传递到 html 文件中进行渲染 admin_list.html
{% extends "layout.html" %}
{% block content %}
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>name</th>
<th>gender</th>
<th>age</th>
<th>department</th>
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.username }}</td>
<td>{{ row.get_gender_display }}</td>
<td>{{ row.age }}</td>
<td>{{ row.depart.title}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
效果图
10.新建用户
在admin_list.html 页面添加一个 a标签,点击跳转到 /admin/add/ 页面, 同时在a 标签的前面添加了一个 bootstrap 官网的 图标
{% extends "layout.html" %}
{% block content %}
<!-- 改动的地方-->
<div style="margin-bottom:10px">
<a class="btn btn-success" href="/admin/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>name</th>
<th>gender</th>
<th>age</th>
<th>department</th>
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.username }}</td>
<td>{{ row.get_gender_display }}</td>
<td>{{ row.age }}</td>
<td>{{ row.depart.title}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
在urls.py 中写相关的视图函数和url 的映射关系
在app/views/admin.py 中写 admin_add 视图函数
admin_add.html
{% extends "layout.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<p>{{ field.label }}: {{ field }} <span style="color:red">{{ field.errors.0 }}</span></p>
{% endfor %}
<input type="submit" value="提交">
</form>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
如上图, 使用 modelform, 和相关的model 有联系,给部门model 设置__str__
,显示 __str__
中的内容。显示每个对象的title
和choice 相关的字段不设置 默认 初始会显示一系列的横杆,设置 default, 显示设置的默认值,如上图,设置default= 1, 默认显示1
admin_add效果图
11.表单的展示
上面的表单不好看, 利用 bootstrp 中的模版,展示一个好看的表单。
面板中套了一个 form 表单(bootstrap 上的模版)
{% extends "layout.html" %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
<div class="panel-body">
<form class="form-horizontal">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span style="color:red">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
还是不好看,可以给添加一个 form-control, 可以 使用两种方式
app\views\admin.py
from django.shortcuts import render
from number_management import models
from django import forms
def admin_list(request):
"""
用户列表
:param request:
:return:
"""
# [obj, obj, obj] order_by("-id") 根据id 倒序排序
queryset = models.Admin.objects.all().order_by("-id")
# for row in queryset:
# 如果 models.py 该字段中定义了 choice 对应关系, 使用对象.get_字段名_display() 获取 choice 中设置的
# 对应的值 例如 row.get_gender_dispaly 可以获取 男或者 女
# choices=[(1, "男"), (2, "女")]
# row.depart.title , 跨表查询
# print(row.username, row.password, row.gender, row.get_gender_display(),row.depart.title)
# mpx 8fe4282d77a078f5a064fff057288ee9 1 男 IT
return render(request,"admin_list.html", {"queryset": queryset})
class AdminModelForm(forms.ModelForm):
class Meta:
model = models.Admin
fields = ["username", "password", "age", "gender", "depart"]
# 给表单增加样式, 字段比较多,加起来有点麻烦
# widgets ={
# 'username': forms.TextInput(attrs={"class": "form-control"})
# }
# 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
def __init__(self, *args, **kwargs):
# 先执行一下父类的init 方法
super().__init__(*args, **kwargs)
# 自定义一些操作, 找到所有的字段
print("----")
print(self.fields)
for name, field_object in self.fields.items():
print(name, field_object)
field_object.widget.attrs = {"class": "form-control"}
def admin_add(request):
form = AdminModelForm()
return render(request, "admin_add.html", {"form": form})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
输入框占一整行也不太好看, 给每个 输入框加一个 row, row 具有统一的边距, 每个form_group, 放到一个div 中,占 整行的6份, 一共12份, 给div 加一个 class="col-xs-6"
{% extends "layout.html" %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
<div class="panel-body">
<form class="form-horizontal">
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-xs-6">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span style="color:red">{{ field.errors.0 }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="row">
<div class="form-group">
<div class="col-xs-6">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
展示错误信息的时候,会把标签挤到下一个位置, 使用 positon:relative 和 position: absolute
相对定位
{% extends "layout.html" %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
<div class="panel-body">
<form class="form-horizontal">
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-xs-6">
<!-- 加一个底部边距可以使 错误信息离输入框远一点-->
<div class="form-group" style="margin-bottom:25px;">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10" style="position:relative">
{{ field }}
<span style="color:red; position:absolute">{{ field.errors.0 }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="row">
<div class="form-group">
<div class="col-xs-6">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
12.新建用户成功
form 表单指定提交方式为 post 方式 admin_add.html
{% extends "layout.html" %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
<div class="panel-body">
<form class="form-horizontal" method="post" novalidate>
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-xs-6">
<div class="form-group" style="margin-bottom:25px;">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10" style="position:relative">
{{ field }}
<span style="color:red; position:absolute">{{ field.errors.0 }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="row">
<div class="form-group">
<div class="col-xs-6">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
用户添加视图函数完成
app/views/admin.py
from django.shortcuts import render, redirect
from django import forms
from number_management import models
from utils.encrypt import md5
def admin_add(request):
if request.method == "GET":
form = AdminModelForm()
return render(request, "admin_add.html", {"form": form})
form = AdminModelForm(data=request.POST)
if not form.is_valid():
return render(request, "admin_add.html", {"form": form})
# 读取密码并更新成md5 加密的新值
form.instance.password = md5(form.instance.password)
# 保存到数据库
form.save()
return redirect("/admin/list/")
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
13.编辑用户
admin_list.html 每一行的数据后面加一个 编辑a标签,点击这个标签,跳转到 admin_edit.html
admin_add.html 和 admin_edit.html 页面的效果是一样的, 可以将 admin_add.html 更改成 admin_form.html, admin_add 和 admin_edit 操作都使用这个页面。
编辑的时候,只能编辑 密码以外的信息, 这个时候, 可以 把 adminModelForm 复制一份,删除其中的
password 字段
class AdminEditModelForm(forms.ModelForm):
class Meta:
model = models.Admin
fields = ["username","age", "gender", "depart"]
# 给表单增加样式, 字段比较多,加起来有点麻烦
# widgets ={
# 'username': forms.TextInput(attrs={"class": "form-control"})
# }
# 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
def __init__(self, *args, **kwargs):
# 先执行一下父类的init 方法
super().__init__(*args, **kwargs)
# 自定义一些操作, 找到所有的字段
print("----")
print(self.fields)
for name, field_object in self.fields.items():
print(name, field_object)
field_object.widget.attrs = {"class": "form-control"}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
编辑页面应该有默认值, admin_edit 视图函数是和 参数拼接到路径中的 url 对应的, 点击 admin_list 相关行的编辑a标签时,会把相关的 用户 id 传递给视图函数, 使用这个 id, 可以从数据库中查找到相关的信息,使用
如上两行指定 form 表单的默认值
from django.contrib import admin
from django.urls import path
from number_management.views import account, admin
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", account.login, name="login"),
path("logout/", account.logout, name="logout"),
path("img/code/", account.img_code, name="img_code"),
path("home", account.home, name="home"),
path("admin/list/", admin.admin_list, name="admin_list"),
path("admin/add/", admin.admin_add, name="admin_add"),
// 参数拼接到路径中
path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
编辑视图函数 app/views/admin.py 中
def admin_edit(request, aid):
# 编辑之前应该有默认值
admin_object = models.Admin.objects.filter(id=aid).first()
if request.method == "GET":
form = AdminEditModelForm(instance=admin_object)
return render(request, "admin_form.html", {"form": form})
form = AdminEditModelForm(instance=admin_object, data=request.POST)
if not form.is_valid():
return render(request, 'admin_form.html', {"form":form})
# 更新
form.save()
return redirect("/admin/list/")
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
14.删除用户
删除时,使用bootstrap 中的 模态框,点击删除弹出一个模态框, 确认是否删除
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
Launch demo modal
</button>
<!-- Modal -->
<div class="modal fade" id="myModal" 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">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
删除时比较危险的操作, 可以使用 警告框的样式。
检查-->定位-->copy 样式,
<!--使用 class 中的内容 class="alert alert-danger alert-dismissible fade in" -->
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4>Oh snap! You got an error!</h4>
<p>Change this and that and try again. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum.</p>
<p>
<button type="button" class="btn btn-danger">Take this action</button>
<button type="button" class="btn btn-default">Or do this</button>
</p>
</div>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
admin_list.html
{% extends "layout.html" %}
{% block content %}
<div style="margin-bottom:10px">
<a class="btn btn-success" href="/admin/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>name</th>
<th>gender</th>
<th>age</th>
<th>department</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.username }}</td>
<td>{{ row.get_gender_display }}</td>
<td>{{ row.age }}</td>
<td>{{ row.depart.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
<!--图标-->
{# <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
编辑
</a>
<input type="button" value="删除" class="btn btn-danger btn-xs" data-toggle="modal"
data-target="#myModal">
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4>Oh snap! You got an error!</h4>
<p>Change this and that and try again. Duis mollis, est non commodo luctus, nisi erat porttitor
ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum.</p>
<p>
<button type="button" class="btn btn-danger">Take this action</button>
<button type="button" class="btn btn-default">Or do this</button>
</p>
</div>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
{% extends "layout.html" %}
{% block content %}
<div style="margin-bottom:10px">
<a class="btn btn-success" href="/admin/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>name</th>
<th>gender</th>
<th>age</th>
<th>department</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.username }}</td>
<td>{{ row.get_gender_display }}</td>
<td>{{ row.age }}</td>
<td>{{ row.depart.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
<!--图标-->
{# <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
编辑
</a>
<input type="button" value="删除" class="btn btn-danger btn-xs" data-toggle="modal"
data-target="#myModal">
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4>是否确认删除?</h4>
<p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
<p>
<button type="button" class="btn btn-danger">确 认</button>
<button type="button" class="btn btn-default">取 消</button>
</p>
</div>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
点击删除按钮, 获取到需要删除的行数据,给按钮绑定相应的点击事件,点击事件触发 模态框弹出
模态框的弹出,可以使用 类属性的方式,也可以使用 javascript 调用的方式
点击 删除按钮,设置,全局删除ID 为 需要删除的 id ,同时弹出模态框
点击 模态框中的确认时, 将需要删除的id 传递给后端的视图函数, 删除数据,重定向到 admin_list.html 页面
点击模态框中的 取消按钮时, 将模态框设置隐藏。
需要给 模态框中的 两个按钮(确认和取消, 绑定 onclick 事件)
{% block js %}
<script>
// 当前删除的 id
DELETE_ID = 0;
function deleteAction(id){
DELETE_ID = id;
console.log(id);
$("#myModal").modal("show");
}
// 确认删除
function confirmDelete(){
// 向后端发送请求, DELETE_ID -> 删除
// 页面刷新
}
//取消删除
function cancelDelete(){
// 全局变量重置成0, 模态框设置隐藏
DELETE_ID = 0;
$("#myModal").modal("hide");
}
</script>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
向后端发送请求的方式,
上面这两种,发送请求的时候, 后刷新页面
ajax 发送请求发送请求的时候, 不会刷新页面
点击模态框的中的确认删除 按钮时, 向后端发送 ajax 请求。
{% block js %}
<script>
// 当前删除的 id
DELETE_ID = 0;
function deleteAction(id){
DELETE_ID = id;
console.log(id);
$("#myModal").modal("show");
}
// 确认删除
function confirmDelete(){
// 向后端发送请求, DELETE_ID -> 删除
// 页面刷新
// admin/delete?aid=1
$.ajax({
url:"admin/delete/",
type:"GET",
data:{
aid:DELETE_ID
},
success:function(res){
console.log("请求返回值:", res);
}
})
}
//取消删除
function cancelDelete(){
// 全局变量重置成0, 模态框设置隐藏
DELETE_ID = 0;
$("#myModal").modal("hide");
}
</script>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
urls.py 中的 url 和视图函数的对应关系
from django.contrib import admin
from django.urls import path
from number_management.views import account, admin
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", account.login, name="login"),
path("logout/", account.logout, name="logout"),
path("img/code/", account.img_code, name="img_code"),
path("home", account.home, name="home"),
path("admin/list/", admin.admin_list, name="admin_list"),
path("admin/add/", admin.admin_add, name="admin_add"),
path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
# 新增的 url
path("admin/delete/<int:aid>/", admin.admin_delete, name="admin_delete")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
app/views/admin.py 中的 admin_delete 视图函数
from django.shortcuts import render, redirect, HttpResponse
from django import forms
from django.http import JsonResponse
from number_management import models
from utils.encrypt import md5
def admin_delete(request):
aid = request.GET.get("aid")
print("要删除的ID:", aid)
models.Admin.objects.filter(id=aid).delete()
# return HttpResponse("11")
return JsonResponse({"status": True})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
admin_list.html
{% extends "layout.html" %}
{% block content %}
<div style="margin-bottom:10px">
<a class="btn btn-success" href="/admin/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>name</th>
<th>gender</th>
<th>age</th>
<th>department</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.username }}</td>
<td>{{ row.get_gender_display }}</td>
<td>{{ row.age }}</td>
<td>{{ row.depart.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/admin/edit/{{ row.id }}/">
<!--图标-->
{# <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
编辑
</a>
<input type="button" value="删除" class="btn btn-danger btn-xs" onclick="deleteAction({{ row.id }});">
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
<h4>是否确认删除?</h4>
<p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
<p>
<button type="button" class="btn btn-danger" onclick="confirmDelete()">确 认</button>
<button type="button" class="btn btn-default" onclick="cancelDelete()">取 消</button>
</p>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
// 当前删除的 id
DELETE_ID = 0;
function deleteAction(id){
DELETE_ID = id;
console.log(id);
$("#myModal").modal("show");
}
// 确认删除
function confirmDelete(){
// 向后端发送请求, DELETE_ID -> 删除
// 页面刷新
// admin/delete?aid=1
$.ajax({
url:"/admin/delete/",
type:"GET",
data:{
aid:DELETE_ID
},
dataType: "JSON",
success:function(res){
if(res.status){
// 删除成功,刷新页面
location.reload();
}else{
alert(res.error);
}
console.log("请求返回值:", res);
}
})
}
//取消删除
function cancelDelete(){
// 全局变量重置成0, 模态框设置隐藏
DELETE_ID = 0;
$("#myModal").modal("hide");
}
</script>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
15.号码管理平台实现思路
参考 用户管理实现
- 号码列表展示
点击导航条上的号码管理a 标签访问 号码管理的 url 和视图函数 ,在视图函数中从数据库中查询所有的号码展示到 号码管理页面
url 和视图的映射关系
from django.contrib import admin
from django.urls import path
from number_management.views import account, admin, number
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", account.login, name="login"),
path("logout/", account.logout, name="logout"),
path("img/code/", account.img_code, name="img_code"),
path("home", account.home, name="home"),
path("admin/list/", admin.admin_list, name="admin_list"),
path("admin/add/", admin.admin_add, name="admin_add"),
path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
path("admin/delete/", admin.admin_delete, name="admin_delete"),
# 新增的 url
path("number/list", number.number_list, name="number_list")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
在 app/views/文件夹下创建一个 number.py 用来存放 number 相关的视图函数
参考 admin_list.html 书写 number_list.html
面板中的 号码展示,
新增号码按钮, 点击访问 url /number/add
编辑号码按钮, 点击访问url /number/edit
删除号码按钮, 点击删除,使用 jquery 绑定事件弹出 模态框,点击模态框中的确认按钮,向 url number/delete 发送ajax 请求(页面不刷新)传递 需要删除的数据行的 id, 删除对应的号码
点击模态框中的取消按钮,使用 jquery 绑定事件,隐藏模态框的展示
# urls.py
from django.urls import path
from number_management.views import account, admin, number
urlpatterns = [
# path('admin/', admin.site.urls),
path("login/", account.login, name="login"),
path("logout/", account.logout, name="logout"),
path("img/code/", account.img_code, name="img_code"),
path("home", account.home, name="home"),
path("admin/list/", admin.admin_list, name="admin_list"),
path("admin/add/", admin.admin_add, name="admin_add"),
path("admin/edit/<int:aid>/", admin.admin_edit, name="admin_edit"),
path("admin/delete/", admin.admin_delete, name="admin_delete"),
path("number/list/", number.number_list, name="number_list"),
path("number/add/", number.number_add, name="number_add"),
path("number/edit/<int:aid>/", number.number_edit, name="number_edit"),
path("number/delete/", number.number_delete, name="number_delete")
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
app/views/number.py
from django import forms
from django.shortcuts import render, redirect
from django.http import JsonResponse
from number_management import models
class numberModelForm(forms.ModelForm):
class Meta:
model = models.Phone
fields = ["mobile", "price", "level", "status", "admin"]
# 给表单增加样式, 字段比较多,加起来有点麻烦
# widgets ={
# 'username': forms.TextInput(attrs={"class": "form-control"})
# }
# 如果类中没有 __init__ 方法,执行父类中的__init__ 方法, 如果有就执行自己的__init__ 方法
def __init__(self, *args, **kwargs):
# 先执行一下父类的init 方法
super().__init__(*args, **kwargs)
# 自定义一些操作, 找到所有的字段
print("----")
print(self.fields)
for name, field_object in self.fields.items():
print(name, field_object)
field_object.widget.attrs = {"class": "form-control"}
def number_list(request):
query_set = models.Phone.objects.all().order_by("-id")
return render(request, "number_list.html", {"query_set": query_set})
def number_add(request):
if request.method == "GET":
form = numberModelForm()
return render(request, "number_form.html", {"form": form})
form = numberModelForm(request.POST)
if not form.is_valid():
return render(request, "number_form.html",{"form": form})
# 保存到数据库
form.save()
return redirect("number_list")
def number_edit(request, aid):
edit_object = models.Phone.objects.filter(id=aid).first()
if request.method == "GET":
form = numberModelForm(instance=edit_object)
return render(request, "number_form.html", {"form": form})
form = numberModelForm(instance=edit_object, data=request.POST)
if not form.is_valid():
return render(request, "number_form.html", {"form": form})
form.save()
return redirect("number_list")
def number_delete(request):
aid = request.GET.get("aid")
models.Phone.objects.filter(id=aid).delete()
return JsonResponse({"status": True})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
number_form.html
{% extends "layout.html" %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-indent-left" aria-hidden="true"></span>表单操作</div>
<div class="panel-body">
<form class="form-horizontal" method="post" novalidate>
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-xs-6">
<div class="form-group" style="margin-bottom:25px;">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10" style="position:relative">
{{ field }}
<span style="color:red; position:absolute">{{ field.errors.0 }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="row">
<div class="form-group">
<div class="col-xs-6">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
number_list.html
{% extends "layout.html" %}
{% block content %}
<div style="margin-bottom:10px">
<a class="btn btn-success" href="/number/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建号码</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Phone</th>
<th>Price</th>
<th>Level</th>
<th>status</th>
<th>admin</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in query_set %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.mobile }}</td>
<td>{{ row.price }}</td>
<td>{{ row.get_level_display }}</td>
<td>{{ row.get_status_display }}</td>
<td>{{ row.admin.username }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/number/edit/{{ row.id }}/">
<!--图标-->
{# <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>#}
编辑
</a>
<input type="button" value="删除" class="btn btn-danger btn-xs" onclick="deleteAction({{ row.id }});">
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
<h4>是否确认删除?</h4>
<p style="margin: 20px 0;">删除后,与之关联的所有相关的数据都会被删除调,确认是否继续?</p>
<p>
<button type="button" class="btn btn-danger" onclick="confirmDelete()">确 认</button>
<button type="button" class="btn btn-default" onclick="cancelDelete()">取 消</button>
</p>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
// 当前删除的 id
DELETE_ID = 0;
function deleteAction(id){
DELETE_ID = id;
console.log(id);
$("#myModal").modal("show");
}
// 确认删除
function confirmDelete(){
// 向后端发送请求, DELETE_ID -> 删除
// 页面刷新
// admin/delete?aid=1
$.ajax({
url:"/number/delete/",
type:"GET",
data:{
aid:DELETE_ID
},
dataType: "JSON",
success:function(res){
if(res.status){
// 删除成功,刷新页面
location.reload();
}else{
alert(res.error);
}
console.log("请求返回值:", res);
}
})
}
//取消删除
function cancelDelete(){
// 全局变量重置成0, 模态框设置隐藏
DELETE_ID = 0;
$("#myModal").modal("hide");
}
</script>
{% endblock %}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
16.总结
项目代码地址: https://github.com/Red-brief/phone_management_platform/tree/main