本套博客基于银角大王武沛齐的django全栈开发视频编写:传送门
如有错误或改进欢迎大家评论、私信交流指正
win+r键,输入cmd
pip install django
Demo1
├── manage.py 【项目的管理,启动项目、创建app、数据管理】【不需要修改】【***常常用***】
├── templates 【存放html静态页面,后期手动创建】
├── static 【图片、css、js、plugins(插件)等前端样式资源,后期手动创建】
└── Demo1
├── __init__.py
├── settings.py 【项目配置文件】 【***常常修改***】
├── urls.py 【URL和函数的对应关系】【***常常修改***】
├── asgi.py 【接收网络请求】【不要动】
└── wsgi.py 【接收网络请求】【不要动】
python manage.py startapp app文件名称
manage.py
文件创建AppDemo1
├── app01
│ ├── __init__.py
│ ├── admin.py 【固定,不用动】django默认提供了admin后台管理。
│ ├── apps.py 【固定,不用动】app启动类
│ ├── migrations 【固定,不用动】数据库变更记录
│ │ └── __init__.py
│ ├── models.py 【**重要**】,对数据库操作,里面的oim代替了原生的Pymysql来操作数据库。
│ ├── tests.py 【固定,不用动】单元测试
│ └── views.py 【最常用】,列:下面执行url就会调用该文件夹的函数。
├── manage.py
└── Demo1
├── __init__.py
├── asgi.py
├── settings.py 【项目配置文件】
├── urls.py 【URL->函数】
└── wsgi.py
'app文件名.apps.类名(apps.py文件中)'
python manage.py runserver 8080
ctrl+c
在浏览器访问,前面127.0.0.1.8080 是默认的,而index是要访问的页面和执行函数路径,具体见配置
在app文件目录下创建一个文件夹templates,里面放置html静态页面
选中templates文件夹,右击new—File—Html,然后取文件名user_list
效果:
render()
并且要导包# 调用函数访问静态页面
def user_list(request):
return render(request,"user_list.html")
HttpResponse()
方法def index(request):
return HttpResponse("欢迎使用Django")
页面是默认到App文件目录templates文件夹中找html文件的,但是可以配置让其到项目目录找
添加方法:
os.path.jion()
os.path.jion()
防止以后做项目需要改文件路径,可以直接通过配置文件修改
<img src="{% 配置文件指定的路径 '文件路径' %}">
<img src="{% static 'img/img.pong' %}">
说明
导包
from django.shortcuts import render
def user_list(request):
return render(request,"user_list.html")
说明
导包
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse("欢迎使用Django") # 结果输出到访问的页面
{% python代码 %}
from django.shortcuts import render,HttpResponse
"""
视图函数
"""
def index(request):
return HttpResponse("欢迎使用Django")
# 调用函数访问静态页面
def user_list(request):
return render(request,"user_list.html")
def tpl(request):
name = "胡图图" # str
roles = ["大胖子","小学生","萝卜头"] # list
user_info={"name":"胡图图","age":3}
return render(request,'tpl.html',{"n1":name,"n2":roles,"n3":user_info}) # 转化
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div>{{ n1 }}}div>
<div>{{ n2 }}}div>
<div>{{ n2.0 }}div>
<div>{{ n2.2 }}div>
<div>
{% for aaa in n2 %}
<span>aaaspan>
{% endfor %}
div>
{{ n3 }}
{{ n3.name }}
{% for foo in n3.foo %}
<li>{{ foo }}li>
{% endfor %}
{% if n1 == "大胖子" %}
<h1>对h1>
{% else %}
<h1>错h1>
{% endif %}
body>
html>
获取前端的请求方式
request.method
Ⅱ、获取get请求传递的值
返回值是字典
# 获取在url上传递的值,get请求 url: http://127.0.0.1:8080/UrlDate/?n=1 传递了n=1
request.GET # 返回值: 返回的是字典
Ⅲ、获取post请求
request.POST
# [响应]将字符串返回给请求者
return HttpResponse("返回内容")
return render(request, 'url_date.html', {"uuu": "大胖子"})
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
{{ uuu }}
body>
html>
# 重定项,访问次函数方法就会跳转页面
return redirect("https://blog.csdn.net/tyx2985491138?type=blog")
from django.shortcuts import render, HttpResponse, redirect
"""
登录页面
"""
def login_a(request):
# 用户第一次访问是以get请求,然后通过前端from表单提交是post请求,从第二次就是post请求
if request.method == "GET":
return render(request,'login.html')
else:
username = request.POST.get("user")
password = request.POST.get("pwd")
if username == 'root' and password == '123456':
# return HttpResponse("登录成功")
return redirect("https://blog.csdn.net/tyx2985491138?type=blog") # 登录成功重定向跳转页面
else:
# return HttpResponse("登录失败")
return render(request,'login.html',{"loginaaa":"用户名密码错误"}) # 使用模板语言让登录失败可以返回到前端
from django.urls import path
from app01 import views # 导入app01中views.py文件(书写函数的文件夹)
urlpatterns = [
# 用户登录案例
path('login/', views.login_a),
]
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录title>
head>
<body>
<h1>用户登录h1>
<form method="post" action="/login/">
{% csrf_token %}
<input type="text" name="user" placeholder="用户名">
<input type="password" name="pwd" placeholder="密码">
<input type="submit" value="提交">
<span style="color: red">{{ loginaaa }}span> <!--用于登录失败后从后端返回显示语句->
form>
body>
html>
import pymysql
# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="root123", charset='utf8', db='unicom')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.发送指令
cursor.execute("insert into admin(username,password,mobile) values('wupeiqi','qwe123','15155555555')")
conn.commit()
# 3.关闭
cursor.close()
conn.close()
pip install mysqlclient
报错解决方法
文件地址:https://wwt.lanzouy.com/ieVdA0es8rad 密码:evzg
mysqlclient安装失败解决方法](http://t.csdn.cn/UetPe)
mysql -u -root -p
或者:mysql -h localhost -u root -p
CREATE DATABASE django;
第78行
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db1', # 数据库名字
'USER': 'root', # 用户名
'PASSWORD': 'root123', # 密码
'HOST': '127.0.0.1', # 安装了MySQL那台机器的host,本地照写
'PORT': 3306,
}
}
Ⅰ、创建表
from django.db import models
# django创建数据库表,django会给翻译为sql语句
class UserInfo(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField()
class Role(models.Model):
caption = models.CharField(max_length=16)
"""
生成的sql语句
"""
# 写了如上的类dajngo会自动生成如下sql语句,表面是app名+类名
create table app01_userinfo(
id bigint auto_increment primary key,
name VARCHAR(32),
password VARCHAR(64)
age int
)
执行命令
生成sql
python manage.py makemigrations
python manage.py migrate
将Role表删掉
在创建表的基础上把models.py文件中,要删除的表的类注释或删除掉即可
from django.db import models
# django创建数据库表,django会给翻译为sql语句
class UserInfo(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField()
# class Role(models.Model):
# caption = models.CharField(max_length=16)
python manage.py makemigrations
python manage.py migrate
class UserInfo(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=64) # varchar字段
# 新增列需要设置默认值
age = models.IntegerField(default=2) # 设置默认值为2
data = models.IntegerField(null=True,blank=True) # int类型,字段名data,可以为空
python manage.py makemigrations
python manage.py migrate
语法
类名或表名.objects.create(字段名='数据')
列:
Department.objects.create(title='销售部')
UserInfo.objects.create(name="大胖子",password="110110110",age=3,data=172)
python manage.py makemigrations
python manage.py migrate
from app01 import models
def orm(request):
# 在Department文件中执行
models.Department.objects.create(title="大胖子")
models.Department.objects.create(title="胡图图")
return HttpResponse("成功")
# 测试orm添加数据,访问orm后端执行sql
path('orm/',views.orm())
# 删除数据
UserInfo.objects.all(id=3).delete() # 删除UserInof表中id为3的数据
Department.objects.all().delete() # 删除全部
语法
类名或表名.objects.all() # 返回值是列表里面封装了对象,每行的对象。每一行数据都是一个对象
# 获得数据
data_list = UserInfo.objects.all() # 返回值是个列表,封装了每一行的对象
for obj in data_list: # 使用for循环遍历,每行的对象并取出每行表的数据
print(obj.id,obj.name,obj.password,obj.age,obj.data)
obj = UserInfo.objects.filter(id=1).first() # 直接返回一个对象
print(obj.id,obj.name,obj.password,obj.age,obj.data)
UserInfo.objects.filter(name="大胖子").update(age=999)
python manage.py startapp app1
pycharm项目终端中
python manage.py startapp app1
填写的是路径
'app01.apps.App01Config'
from django.db import models
"""
部门表
"""
class Department(models.Model):
# 设置自增的,注释掉是因为pycharm创建项目会默认自动创建id
# models.BigAutoField(verbose_name='标题',primar=True)
title = models.CharField(verbose_name='标题', max_length=32) # verbose_name='标题',著名此表头的作用
"""
员工表
"""
class UserInfo(models.Model):
name = models.CharField(verbose_name='姓名', max_length=16)
password = models.CharField(verbose_name='密码', max_length=64) # 字符
age = models.IntegerField(verbose_name="年龄") # 整型
# max_digits:数字长度10,decimal_places:小数有两位,default默认初始值为0
account = models.DecimalField(verbose_name='账户余额', max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name='入职时间')
"""外键部门表"""
# 无约束
# depart_id = models.BigIntegerField(verbose_name='部门表ID')
# 有约束
# - to :表与哪张表关联 , to_fields :表与哪个字段关联
# on_delete=models.CASCADE():表示级联删除,部门表的IT部门被删除了,所属IT部门的员工表的员工会连带删除
# 表头名:depart_id django会根据to_fields自动生成列名
depart = models.ForeignKey(to=Department, to_field="id",on_delete=models.CASCADE
# 置空
# blank=True:可以为空值,
# on_delete=models.SET_NULL():部门表的IT部门被删除了,所属IT部门的员工的部门字段会变为null空值,即无所属部门
# depart = models.ForeignKey(to=Department, to_fields="id", blank=True,on_delete=models.SET_NULL())
"""
在django中约束。在数据库存储的是1和2,约束只能选择哪一部分
"""
gender_choices= (
(1, '男'),
(2, '女')
)
gender = models.SmallIntegerField(verbose_name='性别',choices=gender_choices)
win+r ,输入cmd
mysql -h localhost -u root -p
create database djangoDemo2 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
show databases;
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoDemo2', # 数据库名字
'USER': 'root',
'PASSWORD': '1234',
'HOST': '127.0.0.1', # 哪台机器安装了MySQL
'PORT': 3306,
}
}
python manage.py makemigrations
python manage.py migrate
控制台添加
USE djangodemo2
insert into app_01 app1_department(title) values("销售部"),("IT部");
解压放入app01目录下
static文件:https://wwt.lanzouy.com/iFXts0eu1vre
templates:https://wwt.lanzouy.com/ikXAv0eu1vtg
注意导包,这个项目后面添加的功能不会把导包写出来,只会书写前端没导过的包,从而循循渐进
from django.shortcuts import render
from app1 import models
def depart_list(request):
"""部门列表"""
# 从数据库中获取全部部门信息
query_set = models.Department.objects.all()
return render(request, 'depart_list.html', {'queryset': query_set}) # 把数据库信息返回给前端遍历显示
from django.urls import path
from app1 import views
urlpatterns = [
# path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
]
from django.shortcuts import redirect
def depart_add(request):
if request.method == 'GET':
return render(request, 'depart_add.html')
# 获取用户提交过来的数据
title = request.POST.get("title")
# 保存到数据库
# create(数据库的列名 = 前端提交的数据)
models.Department.objects.create(title=title)
# 重定向,显示所有部门
return redirect('/depart/list/')
# 新增页面
path('depart/add/', views.depart_add),
无修改,使用即可
def depart_delete(request):
# 获取ID
# http://127.0.0.1:8000/depart/delete/?nid=1
nid = request.GET.get('nid')
# 删除
models.Department.objects.filter(id=nid).delete()
# 重定向
return redirect('/depart/list/') # 重定向路径需要在前面也加个 / 千万要注意
# 删除部门
path('depart/delete/', views.depart_delete),
def depart_edit(request, nid):
if request.method == 'GET':
row_update = models.Department.objects.filter(id=nid).first()
print(f"被修改的部门id和名称:{row_update.id, row_update.title}")
return render(request, 'depart_edit.html', {'row_update': row_update.title})
# 获取用户提交的标题
title = request.POST.get("title")
# 根据id找到数据库中的数据并进行更新
models.Department.objects.filter(id=nid).update(title=title)
# 重定向返回主页面
return redirect("/depart/list/")
# 编辑部门
# http://127.0.0.1:8000/depart/4/edit 中间的数字用来分辨是哪个部门
path('depart//edit/' , views.depart_edit),
{% extends 'layout.html' %}
{% block content %}
这里写html页面样式,子类自己独有的
{% endblock %}
可以显示父类的所有页面,并在其基础上进行修改,从而实现自己独特的页面,避免了重复的代码
<h1>这个是父类的,子类继承后也会有h1>
<div>
{% block content %}{% endblock %}
div>
{% extends '父类.html' %}
{% block content %}
{% endblock %}
登录数据库: win+r ,cmd 。mysql -u root -p
选择数据库:USE dangodemo2
insert into app1_userinfo(name,password,age,account,create_time.grdner,depart_id) values("胡图图","110110110",7,100.64,"2022-11-1",2,1);
insert into app1_userinfo(name,password,age,account,create_time.grdner,depart_id) values("大胖子","110110110",7,100.64,"2022-11-1",2,1);
def user_list(request):
# 获取用户全部值
user_info_list = models.UserInfo.objects.all()
# """用python语法,这里是后端的获取数据,加深理解代码 ,把对象传入前端在前端获取值 """
# for obj in user_info_list:
# print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.get_gender_display(),
# obj.depart_id, obj.depart.title)
# """重点"""
# obj.create_time.strftime("%Y-%m-%d") # strftime("%Y-%m-%d"):表显示年月日
# obj.get_gender_display() # 创建员工表时给其定义了,存储数据库1代表是男,2代表是女。这个方法意思是获取规则
# obj.depart.title # 根据id获取,链表的title字段的数据,depart:是models.py文件创建员工表的字段返回值,title则是外键部门表的部门名称字段
return render(request, 'user_list.html', {'user_info_list': user_info_list})
# 员工管理
path('user/list/', views.user_list),
obj.get_gender_display()
**obj.depart.title **
- 用户提交数据没有校验
- 错误,页面上不能显示错误提示
- 页面上每一个字段都需要我们写一遍
- 关联的数据,需要我们手动循环一个一个获取
def user_add(request):
# 访问页面是get请求,提交数据是post请求
if request.method == "GET":
context = {
'gender_choices': models.UserInfo.gender_choices, # 获取所有性别对应的中文
'depart_list': models.Department.objects.all() # 获取所有部门
}
return render(request, 'user_add.html', context) # 将字典返回到前端
# post请求,通过前端表单name值获取每个表单数据
user = request.POST.get("ruser")
pwd = request.POST.get("pwd")
age = request.POST.get("age")
ac = request.POST.get("ac")
ctime = request.POST.get("ctime")
gender = request.POST.get("gd")
dp = request.POST.get("dp")
# 将数据添加到数据库中
models.UserInfo.objects.create(name=user, password=pwd, age=age, account=ac, create_time=ctime, gender=gender,depart_id=dp)
# 添加成功返回用户列表页面
return redirect("/user/list")
# 添加
path('user/add/', views.user_add),
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"> 新建用户 h3>
div>
<div class="panel-body">
<form method="post" >
{% csrf_token %}
<div class="form-group">
<label>姓名label>
<input type="text" class="form-control" placeholder="姓名" name="user" />
div>
<div class="form-group">
<label>密码label>
<input type="text" class="form-control" placeholder="密码" name="pwd"/>
div>
<div class="form-group">
<label>年龄label>
<input type="text" class="form-control" placeholder="年龄" name="age"/>
div>
<div class="form-group">
<label>余额label>
<input type="text" class="form-control" placeholder="余额" name="ac"/>
div>
<div class="form-group">
<label>入职时间label>
<input type="text" class="form-control" placeholder="入职时间" name="ctime"/>
div>
<div class="form-group">
<label>性别label>
<select class="form-control" name="gd">
{% for item in gender_choices %}
<option value="{{ item.0 }}">{{ item.1 }}option>
{% endfor %}
select>
div>
<div class="form-group">
<label>部门label>
<select class="form-control" name="dp">
{% for item in depart_list %}
<option value="{{ item.id }}">{{ item.title }}option>
{% endfor %}
select>
div>
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
简化重复书写表单
class MyForm(Form):
user = forms.CharField(widget=forms.Input)
pwd = form.CharFiled(widget=forms.Input)
email = form.CharFiled(widget=forms.Input)
account = form.CharFiled(widget=forms.Input)
create_time = form.CharFiled(widget=forms.Input)
depart = form.CharFiled(widget=forms.Input)
gender = form.CharFiled(widget=forms.Input)
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request, 'user_add.html',{"form":form})
<form method="post">
{{ form.user }}
{{ form.pwd }}
{{ form.email }}
form>
使用循环加工
<form method="post">
{% for field in form%}
{{ field }}
{% endfor %}
form>
再Form组件的基础上进一步简化
class MyForm(ModelForm): # 只有这里有区别
xxx = form.CharField*("...") # 自定义字段,也支持自定义生成一个字段
class Meta:
model = UserInfo # UserInfo是创建员工表的类名
fields = ["name","password","age","account","create_time","depart","gender","xxx"] # 列表内写的是字段返回值,字段名
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request, 'user_add.html',{"form":form})
class UserInfo(models.Model):
""" 员工表 """
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
<form method="post">
{% for field in form%}
{{ field }}
{% endfor %}
form>
"""
组件实现添加用户
"""
from django import forms
# 使用组件
class UserModelForm(forms.ModelForm):
name = forms.CharField(min_length=3, label="用户名") # 设置name表单接收数据最小值为3
class Meta:
model = models.UserInfo
fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
# 定义表单的class属性
# widgets = {
# "name": forms.TextInput(attrs={"class": "form-control"}),
# "password": forms.TextInput(attrs={"class": "form-control"}),
# }
# 简化上面注释的代码
# 批量生成,让字段加样式
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环获取所有插件
for name, field in self.fields.items(): # 这里的name是字段名
# if name == "password": # 让password这个字段不加class
# continue
field.widgets.attrs = {"class": "form-control", "placeholder": field.labels}
def user_model_form_add(request):
if request.method == "GET":
form = UserModelForm()
return render(request, 'user_model_form_add.html', {'form': form})
# 用户使用post提交数据,数据校验
form = UserModelForm(data=request.POST)
if form.is_valid():
# 逐一校验,数据合法存储入数据库
# print(form.changed_data)
form.save()
return redirect('/user/list/')
else:
print(form.errors) # 将错误信息打印出来
return render(request, 'user_model_form_add.html', {'form': form})
# 组件实现添加
path('user/model/form/add', views.user_model_form_add)
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"> 新建用户 h3>
div>
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}span>
div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
from django.db import models
# Create your models here.
class Department(models.Model):
"""部门表"""
# models.BigAutoField(verbose_name='标题',primar=True) # 设置自增的,注释掉是因为pycharm创建项目会默认自动创建
title = models.CharField(verbose_name='标题', max_length=32) # verbose_name='标题',著名此表头的作用
# 类似于to.string方法
def __str__(self):
return self.title
class UserInfo(models.Model):
"""员工表"""
name = models.CharField(verbose_name='姓名', max_length=16)
password = models.CharField(verbose_name='密码', max_length=64) # 字符
age = models.IntegerField(verbose_name="年龄") # 整型
account = models.DecimalField(verbose_name='账户余额', max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name='入职时间')
depart = models.ForeignKey(verbose_name='部门', to=Department, to_field="id", on_delete=models.CASCADE)
gender_choices = (
(1, '男'),
(2, '女')
)
gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
使用组件
def user_edit(request, nid):
row_object = models.UserInfo.objects.filter(id=nid).first() # 用于告诉ModelForm是要修改哪一行数据,而不是添加
# 根据id获取要编辑的那一行数据,从而将原来的值显示在页面上
if request.method == 'GET':
# row_object = models.UserInfo.objects.filter(id=nid).first()
form = UserModelForm(instance=row_object)
name = row_object.name
return render(request, 'user_edit.html', {'form': form, 'name': name}) # 因为form是字典所以可以直接返回,不用大括号
# post提交表单请求,并数据校验
# row_object = models.UserInfo.objects.filter(id=nid).first() # 用于告诉ModelForm是要修改哪一行数据,而不是添加
form = UserModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save() # 数据合法存储入数据库
return redirect('/user/list/')
else:
return render(request, 'user_edit.html', {'form': form})
# 编辑
path('user//edit/' , views.user_edit),
需要自己创建
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" > 编辑<font color="#1e90ff">{{ name }}font>用户 h3>
div>
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}span>
div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
"""
删除用户
"""
def user_delete(request):
# 获取ID
# http://127.0.0.1:8000/depart/delete/?nid=1
nid = request.GET.get('nid') # 获取删除按钮通过表单传达过来的nid值
print(nid)
models.UserInfo.objects.filter(id=nid).delete()
return redirect('/user/list/')
# 删除
path('user/delete/', views.user_delete),
class PrettyNum(models.Model):
mobile = models.CharField(verbose_name="手机号", max_length=11)
# 想要允许为空 null=True ,blank = True
price = models.IntegerField(verbose_name="价格")
# 级别
level_choice = (
(1, '1级'),
(2, '2级'),
(3, '3级'),
(4, '4级'),
)
level = models.SmallIntegerField(verbose_name="级别", choices=level_choice, default=1) # 小整型
status_choices = (
(1, '已占用'),
(2, '未占用'),
)
status = models.SmallIntegerField(verbose_name="级别", choices=status_choices, default=2)
将ID、号码、价格、级别(中文)、状态(中文)、操作(编辑和删除)展现出来
views.py
def admin_list(request):
# order_by()设置排序,select * from 表名 order by level desc
queryset = models.PrettyNum.objects.all().order_by("-level") # 加-则表示desc,不加-则表示level
return render(requesret, 'admin_list.html', {'queryset': queryset})
# 靓号列表主页面
path('admin/list/', views.admin_list),
{% extends 'layout.html ' %}
{% block content %}
<div class="container">
<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>
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true">span>
靓号列表
div>
<table class="table table-bordered">
<thead>
<tr>
<th>IDth>
<th>号码th>
<th>价格th>
<th>级别th>
<th>状态th>
<th>操作th>
tr>
thead>
<tbody>
{% for obj in queryset %}
<tr>
<th>{{ obj.id }}th>
<td>{{ obj.mobile }}td>
<td>{{ obj.price }}td>
<td>{{ obj.get_level_display }}td>
<td>{{ obj.get_status_display }}td>
<td>
<a class="btn btn-primary btn-xs" href="/admin/{{ obj.id }}/edit/">编辑a>
<a class="btn btn-danger btn-xs" href="/admin/delete/?nid={{ obj.id }}">删除a>
td>
tr>
{% endfor %}
tbody>
table>
div>
div>
{% endblock %}
通过正则表达式校验数据
views.py
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class UserModForm2(forms.ModelForm):
# 验证:方法1
# mobile = forms.CharField(
# label="手机号",
# validators=[RegexValidator(r'^\d{11}$', '手机号必须为11位数')] # 使用正则表达式对其加强校验
# )
class Meta:
model = models.PrettyNum # 选择表
fields = ["mobile", "price", "level", "status"] # 这个是自己选哪些字段
# fields = "__all__" # 这个是所有字段
# exclude = ['level'] # 这个是排除哪个字段
"""加载样式"""
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# # 循环获取所有插件
# for name, field in self.fields.items(): # 这里的name是字段名
# # if name == "password": # 让password这个字段不加class
# # continue
# # 设置生成的表单class值为"form-control"
# field.widgets.attrs = {"class": "form-control", "placeholder": field.labels}
# 验证:方法2
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"] # 获取"mobile"字段用户输入的值
if len(txt_mobile) != 11:
# 验证不通过
raise ValidationError("格式错误")
# 验证通过,用户输入的值返回
return txt_mobile
def admin_add(request):
if request.method == 'GET':
form = UserModForm2()
return render(request, 'admin_add.html', {'form': form})
# post请求
form = UserModForm2(data=request.POST)
if form.is_valid(): # 一个一个校验数据
form.save() # 添加进入数据库
return redirect('/admin/list/')
else:
print(form.errors) # 打印错误信息
return render(request, 'admin_add.html', {'form': form})
# 添加
path('admin/add/', views.admin_add),
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"> 添加靓号 h3>
div>
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}span>
div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
电话号码不可编辑,但还是要显示到页面上
点击编辑后会携带靓号的id到后端从而修改
views.py
def admin_edit(request, nid):
row_object = models.PrettyNum.objects.filter(id=nid).first()
if request.method == "GET":
form = UserModForm2(instance=row_object)
return render(request, 'admin_edit.html', {'form': form})
form = UserModForm2(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/admin/list')
else:
return render(request, 'admin_edit.html', {'form': form})
# 编辑
path('admin//edit/' , views.admin_edit),
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" > 编辑靓号 h3>
div>
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}span>
div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
def admin_delete(request):
nid = request.GET.get('nid') # 前端的?nid = {{obj.id}} ,?是占位符代表给nid赋值
models.PrettyNum.objects.filter(id=nid).delete()
return redirect('/admin/list')
# 删除
path('admin/delete/', views.admin_delete)
def admin_list(request):
"""查询所有"""
# order_by()设置排序,select * from 表名 order by level desc
# queryset = models.PrettyNum.objects.all().order_by("-price") # 加-则表示正序,不加-则表示倒叙,按价格从高到低
# return render(request, 'admin_list.html', {'queryset': queryset})
"""搜索查询"""
data_dict = {}
search_data = request.GET.get('q', "")
if search_data:
data_dict["mobile__contains"] = search_data
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price") # 按条件查询
return render(request, 'admin_list.html', {'queryset': queryset, 'search_data': search_data})
# 靓号列表主页面
path('admin/list/', views.admin_list),
将原主页修改
搜索框样式是bootstrap:官网https://v3.bootcss.com/ 样式包(bootstrap-3.4.1):https://wwt.lanzouy.com/iYMpj0f7fgze
导入样式包(bootstrap-3.4.1)
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script>
<div style="float: right;width: 300px">
<form method="get">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="搜索号码"
value="{{ search_data }}">
<span class="input-group-btn">
<button class="btn b tn-default" type="submit">Go!button>
span>
form>
div>
写在views.py的admin_list函数中,访问即可给数据库添加300条数据
# 添加300条数据
for i in range(300):
models.PrettyNum.objects.create(mobile="16656678912", price=10, level=1, status=1)
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[初始分页索引:结尾id]
def admin_list(request):
"""查询所有"""
# order_by()设置排序,select * from 表名 order by level desc
# queryset = models.PrettyNum.objects.all().order_by("-price") # 加-则表示正序,不加-则表示倒叙,按价格从高到低
# return render(request, 'admin_list.html', {'queryset': queryset})
"""搜索查询"""
data_dict = {}
search_data = request.GET.get('q', "")
if search_data:
data_dict["mobile__contains"] = search_data
"""分页"""
# 获取get请求传递的值,page默认值为1,当前页码
x = int(request.GET.get('p', 1)) # 搜索页码的值
if x > 1:
page = x
page = int(request.GET.get('page', 1)) # 页码输入的当前页码
# 当前页码,根据用户想要访问的页面,计算出起止位置
page_size = 10 # 显示10条数据
start = (page - 1) * page_size
end = page * page_size
# 按条件查询
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[start:end]
# 数据总条数
total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-price").count()
"""页码"""
# 总页码 = 数据总条数/每页显示数据条数
total_page_count, div = divmod(total_count, page_size)
# if div:
# total_page_count += 1
# 计算,显示出当前页的前五页和后五页
plus = 5 # 显示前后页码条数
if total_page_count <= 2 * plus + 1: # 小于11页
# 数据库数据较少没有超过十一页,把所有页码显示
start_page = 1
end_page = total_page_count
else:
# 数据库数据大于11页数据
# 当前页小于5(小极值)
if page <= plus: # page是前端用户传入要访问的页数
start_page = 1
end_page = 2 * plus + 1
else:
# 当前页+显示条数>总页码
if (page + plus) > total_page_count:
start_page = total_page_count - 2 * plus
end_page = total_page_count
else:
start_page = page - plus # 当前页减5
end_page = page + plus # 后五页
# 自动生成页码(前端样式)
page_str_list = []
# 上一页
if page > 1:
prev = '上一页 '.format(page - 1) # 当前页减一
else:
prev = '上一页 '.format(1)
page_str_list.append(prev)
# for i in range(1, total_page_count + 1): # 初始页面和总页码
for i in range(start_page, end_page + 1): # +1 因为数据库搜索出来数据总数会少1
if i == page: # 判断是当前页的话给其添加样式
elc = '{} '.format(i, i)
else:
elc = '{} '.format(i, i)
page_str_list.append(elc) # 将添加进列表中
# 下一页
if page < total_page_count:
prev = '下一页 '.format(page + 1) # 当前页加1
else:
prev = '下一页 '.format(total_page_count) # 总页码
page_str_list.append(prev)
# 尾页
one = '尾页 '.format(total_page_count)
page_str_list.append(one)
# 使用mark_safe包裹就可以将字符串转化成html,在导包的前提下from django.utils.safestring import mark_safe
page_string = mark_safe("".join(page_str_list))
return render(request, 'admin_list.html',
{'queryset': queryset, 'search_data': search_data, "page_string": page_string})
不做更改
<ul class="pagination">
{# <li><a href="?page=1">1a>li>#}
{# <li><a href="?page=2">2a>li>#}
{# <li><a href="?page=3">3a>li>#}
{# <li><a href="?page=4">4a>li>#}
{# <li><a href="?page=5">5a>li>#}
{{ page_string }}
<li>
<form method="get">
<div class="input-group">
<input type="text" name="p" class="form-control" placeholder="搜索号码"
value="{{ search_data }}">
<span class="input-group-btn">
<button class="btn b tn-default" type="submit">Go!button>
span>
div>
form>
li>
ul>
def admin_list(request):
"""查询所有"""
# order_by()设置排序,select * from 表名 order by level desc
# queryset = models.PrettyNum.objects.all().order_by("-price") # 加-则表示正序,不加-则表示倒叙,按价格从高到低
# return render(request, 'admin_list.html', {'queryset': queryset})
"""搜索查询"""
data_dict = {}
search_data = request.GET.get('q', "")
if search_data:
data_dict["mobile__contains"] = search_data
"""分页"""
from app1.utils.pagination import Pagination
# 按条件查询
# queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")[page_object.start: page_object.end]
queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-price")
page_object = Pagination(request, queryset) # 获取自定义组件类
page_queryset = page_object.page_queryset # 获取分页完的数据
page_string = page_object.html() # 生成的页码
context = {
"queryset": page_queryset,
"search_date": search_data, # 分完页的数据
"page_string": page_string # 生成的页码
}
return render(request, 'admin_list.html', context) # 使用组件后
在app1中创建utils文件夹里面创建paginaion.py
将页码生成、数据获取、前端页码生成封装到pagination.py文件中Pagination类中
"""
自定义分页组件
"""
from django.utils.safestring import mark_safe
from django import forms
class Pagination(object):
def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):
"""
:param request: 请求的对象
:param queryset:查询的数据,符合条件的数据对这个进行分页处理
:param page_size:每页显示多条数据
:param page_param:在URL中传递获取分页的参数,列如:/etty/list/?page=12
:param plus:显示当前页的,前、后几页(页码)
"""
import copy
query_dict = copy.deepcopy(request.GET)
query_dict._mutable = True
self.query_dict = query_dict
self.page_param = page_param
page = request.GET.get(page_param, "1") # 把request获取前端get请求的方法,封装到page中,页码
if page.isdecimal(): # 处理页码,判断页码是否是正常传入数字,而不是字符串
page = int(page)
else:
page = 1 # 前端传入页码不规范,则默认为1
self.page = page
self.page_size = page_size
# 计算分页页码,sql值
self.start = (page - 1) * page_size
self.end = page * page_size
self.page_queryset = queryset[self.start: self.end] # 分完页的数据
# total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-price").count()
total_count = queryset.count() # 数据总条数
total_page_count, div = divmod(total_count, page_size) # 总页码 = 数据总条数/每页显示数据条数
if div:
total_page_count += 1
self.total_page_count = total_page_count
self.plus = plus # 显示前后页码条数
def html(self):
"""
页码、页码搜索
"""
# 计算出,显示当前页的前5页、后5页
if self.total_page_count <= 2 * self.plus + 1:
# 数据库中的数据比较少,都没有达到11页。
start_page = 1
end_page = self.total_page_count
else:
# 数据库中的数据比较多 > 11页。
# 当前页<5时(小极值)
if self.page <= self.plus:
start_page = 1
end_page = 2 * self.plus + 1
else:
# 当前页 > 5
# 当前页+5 > 总页面
if (self.page + self.plus) > self.total_page_count:
start_page = self.total_page_count - 2 * self.plus
end_page = self.total_page_count
else:
start_page = self.page - self.plus
end_page = self.page + self.plus
# 页码
page_str_list = []
self.query_dict.setlist(self.page_param, [1])
page_str_list.append('首页 '.format(self.query_dict.urlencode()))
# 上一页
if self.page > 1:
self.query_dict.setlist(self.page_param, [self.page - 1])
prev = '上一页 '.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [1])
prev = '上一页 '.format(self.query_dict.urlencode())
page_str_list.append(prev)
# 页面
for i in range(start_page, end_page + 1):
self.query_dict.setlist(self.page_param, [i])
if i == self.page:
ele = '{} '.format(self.query_dict.urlencode(), i)
else:
ele = '{} '.format(self.query_dict.urlencode(), i)
page_str_list.append(ele)
# 下一页
if self.page < self.total_page_count:
self.query_dict.setlist(self.page_param, [self.page + 1])
prev = '下一页 '.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [self.total_page_count])
prev = '下一页 '.format(self.query_dict.urlencode())
page_str_list.append(prev)
# 尾页
self.query_dict.setlist(self.page_param, [self.total_page_count])
page_str_list.append('尾页 '.format(self.query_dict.urlencode()))
search_string = """
"""
page_str_list.append(search_string)
page_string = mark_safe("".join(page_str_list))
return page_string
插件链接:https://wwt.lanzouy.com/imuOd0fe3opc
用于美化、简化入职时间填写,可以直接通过点击xuan’ze
重点在导包,和函数路径
from django.contrib import admin
from django.urls import path
from app1 import views
from app1.views import admin, list, user # 导入views文件
urlpatterns = [
# path('admin/', admin.site.urls),
# 部门管理
# 主页面、列表页面
path('depart/list/',list.depart_list),
# 新增部门
path('depart/add/', list.depart_add),
# 删除部门
path('depart/delete/', list.depart_delete),
# 编辑部门
# http://127.0.0.1:8000/depart/4/edit 中间的数字用来分辨是哪个部门
path('depart//edit/' , list.depart_edit),
# 员工管理
path('user/list/', user.user_list),
# 添加
path('user/add/', user.user_add),
# 组件实现添加
path('user/model/form/add', user.user_model_form_add),
# 编辑
path('user//edit/' , user.user_edit),
# 删除
path('user/delete/', user.user_delete),
# 靓号管理
# 靓号列表主页面
path('admin/list/', admin.admin_list),
# 添加
path('admin/add/', admin.admin_add),
# 编辑
path('admin//edit/' , admin.admin_edit),
# 删除
path('admin/delete/', admin.admin_delete)
]
class VIP(models.Model):
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
python manage.py makemigrations
python manage.py migrate
在views文件夹中创建VIP.py文件
from django.shortcuts import render, redirect
from app1 import models
# 分页自定义模块
from app1.utils.pagination import Pagination
"""
管理员列表
"""
def VIP_list(request):
# 搜索功能
# 构造搜索条件
data_dict = {}
search_data = request.GET.get('q', "")
if search_data:
data_dict["username__contains"] = search_data
# 根据条件去数据库获取
queryset = models.VIP.objects.filter(**data_dict)
# 分页
page_object = Pagination(request, queryset)
queryset = {
'queryset': queryset,
'page_string': page_object.html(),
'search_data': search_data,
}
return render(request, 'VIP_list.html', queryset)
# 主页面
path('VIP/list', VIP.VIP_list),
{% extends 'layout.html ' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px" class="clearfix">
<a class="btn btn-success" href="/VIP/add">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true">span>
添加管理员
a>
<div style="float: right;width: 300px">
<form method="get">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="关键字"
value="{{ search_data }}">
<span class="input-group-btn">
<button class="btn b tn-default" type="submit">搜索查询button>
span>
div>
form>
div>
div>
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true">span>
管理员账户列表
div>
<table class="table table-bordered">
<thead>
<tr>
<th>IDth>
<th>号码th>
<th>密码th>
<th>重置密码th>
<th>操作th>
tr>
thead>
<tbody>
{% for obj in queryset %}
<tr>
<th>{{ obj.id }}th>
<td>{{ obj.username }}td>
<td>********td>
<td>
<a href="/VIP/{{obj.id}}/reset/">重置密码a>
td>
<td>
<a class="btn btn-primary btn-xs" href="/VIP/{{ obj.id }}/edit">编辑a>
<a class="btn btn-danger btn-xs" href="/VIP/delete/?nid={{ obj.id }}">删除a>
td>
tr>
{% endfor %}
tbody>
table>
div>
<ul class="pagination">
{{ page_string }}
ul>
div>
{% endblock %}
重点:密码校验
VIP.py
views文件夹下
from django import forms
from django.core.exceptions import ValidationError
from app1.utils.bootstrap import BootStrapModeForm # 创建input框
from app1.utils.encrypt import md5 # 加密
# 使用组件创建input框
class VIPModelForm(BootStrapModeForm): # 或者继承forms.Modelform
# 额外生成一个确认密码表单
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True) # 输入密码时候不会显示密码,render_value=True表示密码错了密码不需要重新输入
)
class Meta:
model = models.VIP
fields = ["username", "password", "confirm_password"]
# 给原有字段,生成的表单添加额外修饰
widgets = {
"password": forms.PasswordInput(render_value=True)
}
# 加密
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd) # 调用加密函数给input框的数据加密,并将加完密的函数返回,则下面方法获取该表单都是加密了的
# 钩子函数,获取指定字段用户输入的值
def clean_confirm_password(self):
# form.cleaned_data 获取校验成功后所有数据,具体看上面定义的clean_confirm_password钩子函数
password = self.cleaned_data.get("password") # 因为上面给表单加密了,所以这里获取的值是加密的值
confirm = md5(self.cleaned_data.get("confirm_password")) # 获取确认用户表单,用户输入的值,并使其加密从而可以使密文和密文进行比较
if confirm != password:
raise ValidationError("密码不一致") # 抛出异常到前端,需要导包
return confirm # 这里返回的值就是存储入数据库的值
def VIP_add(request):
title = "添加管理员"
if request.method == "GET":
form = VIPModelForm()
return render(request, 'change.html', {"title": title, "form": form})
# post请求校验数据,并跳转主页面
form = VIPModelForm(data=request.POST)
if form.is_valid():
# 上面的钩子函数也是校验数据
form.save()
return redirect('/VIP/list')
# 有错误信息,则还是在添加页面,并可以看到错误信息
return render(request, 'change.html', {"title": title, "form": form})
# 添加管理员
path('VIP/add', VIP.VIP_add),
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"> {{ title }}h3>
div>
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}label>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}span>
div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交button>
form>
div>
div>
div>
{% endblock %}
import hashlib
from django.conf import settings
"""
给字符串加密
"""
def md5(data_string):
obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8')) # SECRET_KEY是django的配置文件Setting随机生成的一个字符串
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()
views文件夹子文件
class VIPEditModelForm(BootStrapModeForm):
class Meta:
model = models.VIP
fields = ["username"] # 只允许修改用户名
def VIP_edit(request, nid):
title = "编辑管理员"
row_object = models.VIP.objects.filter(id=nid).first()
if not row_object: # 判断是否有这个用户,没有就证明id为错误的
return redirect('/VIP/list')
if request.method == "GET":
form = VIPEditModelForm(instance=row_object) # 将原来的值显示在页面上
return render(request, 'change.html', {"title": title, "form": form})
# 提交数据
form = VIPEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/VIP/list')
return render(request, 'change.html', {"title": title, "form": form})
# 编辑
path('VIP//edit/' , VIP.VIP_edit),
def VIP_delete(request):
nid = request.GET.get('nid')
models.VIP.objects.filter(id=nid).delete()
return redirect('/VIP/list')
# 删除
path('VIP/delete/', VIP.VIP_delete),
# 使用组件创建input框
class VIPresetModelForm(BootStrapModeForm): # 或者继承forms.Modelform
# 额外生成一个确认密码表单
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True) # 输入密码时候不会显示密码,render_value=True表示密码错了密码不需要重新输入
)
class Meta:
model = models.VIP
fields = ["password", "confirm_password"]
# 给原有字段,生成的表单添加额外修饰
widgets = {
"password": forms.PasswordInput(render_value=True)
}
# 加密
def clean_password(self):
pwd = self.cleaned_data.get("password")
md5_pwd = md5(pwd) # 调用加密函数给input框的数据加密,并将加完密的函数返回,则下面方法获取该表单都是加密了的
# 去数据库校验之前密码和新输入的密码是否一致
exists = models.VIP.objects.filter(id=self.instance.pk, password=md5_pwd) # 返回的是布尔值
if exists:
raise ValidationError("密码使用过,请尝试重新输入密码")
return md5_pwd
# 钩子函数,获取指定字段用户输入的值
def clean_confirm_password(self):
# form.cleaned_data 获取校验成功后所有数据,具体看上面定义的clean_confirm_password钩子函数
password = self.cleaned_data.get("password") # 因为上面给表单加密了,所以这里获取的值是加密的值
confirm = md5(self.cleaned_data.get("confirm_password")) # 获取确认用户表单,用户输入的值,并使其加密从而可以使密文和密文进行比较
if confirm != password:
raise ValidationError("密码不一致") # 抛出异常到前端,需要导包
return confirm # 这里返回的值就是存储入数据库的值
def VIP_reset(request, nid):
row_object = models.VIP.objects.filter(id=nid).first()
if not row_object:
return redirect('/VIP/list')
title = "重置用户:{} 密码".format(row_object.username)
if request.method == 'GET':
form = VIPresetModelForm()
return render(request, 'change.html', {"title": title, "form": form})
# 提交数据
form = VIPresetModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect('/VIP/list')
return render(request, 'change.html', {"title": title, "form": form})
# 重置密码
path('VIP//reset/' , VIP.VIP_reset),
自定义的工具包,因为重置密码使用的是form而不是ModelForm所以使用继承改装一下
from django import forms
"""
添加样式,实际是给每个表单创建class属性
"""
class BootStrap:
def __int__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环Modelform中所有字段,给每个字段的插件设置
for name, field in self.field.items():
# 字段中有属性,保留原来的属性,没有属性才增加
if field.widget.attrs:
field.widget.attrs["class"] = "form-control"
field.widget.attrs["placeholder"] = field.label
else:
field.widget.attrs = {
"class": "form-control",
"placeholder": field.label
}
class BootStrapModeForm(BootStrap, forms.ModelForm):
pass
class BootStrapForm(BootStrap, forms.Form):
pass
from django.shortcuts import render, redirect, HttpResponse
from app1.utils.encrypt import md5
from app1 import models
"""
登录
"""
# 使用form做
from django import forms
from app1.utils.bootstrap import BootStrapForm
class LoginForm(BootStrapForm): # Form需要自定义字段,
username = forms.CharField(
label="用户名",
widget=forms.PasswordInput(attrs={"class": "form-control"}) # 定义标签
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(attrs={"class": "form-control"}, render_value=True) # 密码标签
)
# 钩子函数,给密码加密,从而可以使数据与后端密码比较
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
# # ModelForm则是从数据库中拿字段,去数据库拿安全性差些
# class LoginModelForm(forms.ModelForm):
# class Meta:
# model = models.VIP
# fields = ['username', 'password']
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, 'login.html', {'form': form})
form = LoginForm(data=request.POST)
if form.is_valid():
# form.cleaned_data获取前端用户输入的值(用户名和密码)
# print(form.cleaned_data) # 验证成功,获取到的用户名和密码,因为form与modelForm不同,form不与数据库关联不可以直接写入数据库
# 去数据库校验用户名和密码是否正确
# 因为上面有个钩子函数,给form.cleaned_data方法加密了,所以这个获取的也是加密的密码
first = models.VIP.objects.filter(**form.cleaned_data).first()
# first = models.VIP.objects.filter(username="xxx", password="xxx").first() ,如上代替具体见搜索手机号详解,只有字典可以这么用
if not first:
form.add_error("password", "用户密码错误") # 给password字段主动添加错误,password从上面18行创建字段表单获取的
# 存储session到数据库django_session表中自动创建的,也就是用户cookie的凭证,使用cookie的时候得用session来辨别用户
# request.session["info"] = {'id': filter.id, 'username': first.username} # 获取用户id和username存储到session中,来当用户凭证
request.session["info"] = first.username # 这里只存储个username
return redirect("/VIP/list/")
return render(request, 'login.html', {'form': form})
# 登录
path('login/', login.login),
{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
<style>
.account {
width: 400px;
border: 1px solid #dddddd;
border-radius: 5px;
box-shadow: 5px 5px 20px #aaa;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
padding: 20px 40px;
}
.account h2 {
margin-top: 10px;
text-align: center;
}
style>
head>
<body>
<div class="account">
<h2>用户登录h2>
<form method="post" novalidate>
{% csrf_token %}
<div class="form-group">
<label>用户名label>
{{ form.username }}
<span style="color: red;">{{ form.username.errors.0 }}span>
div>
<div class="form-group">
<label>密码label>
{{ form.password }}
<span style="color: red;">{{ form.password.errors.0 }}span>
div>
<input type="submit" value="登 录" class="btn btn-primary">
form>
div>
body>
html>
request.session["info"] = {'id': filter.id, 'username': first.username} # 获取用户id和username存储到session中,来当用户凭证
request.session["info"] = first.username # 这里只存储个username
原理是通过session来获取cookie,如果没有cookie就没有登录过
# 校验是否登录,没登陆跳转登录页面
info = request.session.get("info") # 获取session的info字典的用户cookie凭证
if not info:
return redirect('/login/')
因为中间键的使用所以,不需要每个views.py中都使用如上代码校验登录。只需要定义一个中间件即可,函数文件中代码不需要改变
在app1下创建个文件夹middleware,再创建个auth.py。里面代码如下
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect
# 中间件校验用户是否登录
class AuthMiddleware(MiddlewareMixin):
"""中间件1"""
def process_request(self, request):
# 排除那些不需要登录校验的页面
if request.path_info == "/login/":
return
# 读取当前访问的用户session信息,如若读到证明登录过,则继续执行
info_dict = request.session.get("info")
if info_dict:
return
# 没有登录信息
return redirect('/login/')
setting.py文件中,每个类是一个中间件
删除cookie即可,因为没有cookie需要重新登录
def logout(request):
request.session.clear() # 将当前用户的session删除掉,等于将用户cookie删除
return redirect('/login/')
# 注销
path('logout/', login.logout),
将html的注销标签的执行函数地址改一下
<li><a href="/logout/">注销a>li>
随着用户改变用户名也会跟着自动改变
# 通过session来获取当前用户的账号和密码
info = request.session.get("info") # 获取session的info字典的用户cookie凭证
# 取值,存储在session中用户输入的账号和密码
info["id"]
info["username"]
{{ request.session.info.username }}
自动生成验证码图片插件
pip install pillow
或者到pycham设置中项目-python解释器中安装
参考文献:https://www.cnblogs.com/wupeiqi/articles/5812291.html
生成验证码的字体:
自定义的工具包文件,注意修改第4行的字体路径,这个字体是和app1在同一个目录下
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
def rndChar():
"""
生成随机字母
:return:
"""
# return str(random.randint(0, 9)) # 生成数字
return chr(random.randint(65, 90)) # 生成字母
def rndColor():
"""
生成随机颜色
:return:
"""
return random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)
# 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
# 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
# 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
# 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor())
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return img, ''.join(code)
<div class="form-group">
<label for="id_code">图片验证码label>
<div class="row">
<div class="col-xs-7">
{# <input type="text" name="code" class="form-control" placeholder="请输入图片验证码"required="" id="id_code">#}
{# <span style="color: red;">span>#}
{{ form.code }}
<span style="color: red">{{ form.code.errors.0 }}span>
div>
<div class="col-xs-5">
<img id="image_code" src="image/code/" style="width: 125px;">
div>
div>
div>
核心代码53-58
from django.shortcuts import render, redirect, HttpResponse
from app1.utils.encrypt import md5
from app1 import models
from app1.utils.code import check_code
from io import BytesIO
# 使用form做
from django import forms
from app1.utils.bootstrap import BootStrapForm
class LoginForm(BootStrapForm): # Form需要自定义字段,
username = forms.CharField(
label="用户名",
widget=forms.TextInput(attrs={"class": "form-control"}) # 定义标签
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(attrs={"class": "form-control"}, render_value=True) # 密码标签
)
code = forms.CharField(
label="验证码",
widget=forms.TextInput,
required=True
)
# 钩子函数,给密码加密,从而可以使数据与后端密码比较
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
# # ModelForm则是从数据库中拿字段,去数据库拿安全性差些
# class LoginModelForm(forms.ModelForm):
# class Meta:
# model = models.VIP
# fields = ['username', 'password']
"""
登录
"""
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, 'login.html', {'form': form})
form = LoginForm(data=request.POST)
if form.is_valid():
# form.cleaned_data获取前端用户输入的值(用户名和密码、验证码)
# print(form.cleaned_data) # 验证成功,获取到的用户名和密码,因为form与modelForm不同,form不与数据库关联不可以直接写入数据库
# 验证码校验,并使用pop()把code验证码的值剔除掉,从而保证其不会被存储入数据库
user_input_code = form.cleaned_data['code'] # 用户填写的验证码
code = request.session.get('image_code', "")
if code == user_input_code:
form.add_error("code", "验证码错误")
return render(request, 'login.html', {'form': form})
# 去数据库校验用户名和密码是否正确
# 因为上面有个钩子函数,给form.cleaned_data方法加密了,所以这个获取的也是加密的密码
first = models.VIP.objects.filter(**form.cleaned_data).first()
# first = models.VIP.objects.filter(username="xxx", password="xxx").first() ,如上代替具体见搜索手机号详解,只有字典可以这么用
if not first:
form.add_error("password", "用户密码错误") # 给password字段主动添加错误,password从上面18行创建字段表单获取的
# 存储session到数据库django_session表中自动创建的,也就是用户cookie的凭证,使用cookie的时候得用session来辨别用户
request.session["info"] = {'username': first.username} # 获取用户id和username存储到session中,来当用户凭证,这里只存储username
# 设置session存活时间,7天内免登录
request.session.set_expiry(60 * 60 * 24 * 7)
return redirect("/VIP/list/")
return render(request, 'login.html', {'form': form})
"""
验证码
"""
def image(request):
# 调用pillow函数,生成图片
img, code_string = check_code()
# 将生成的验证码写入session中,方便后续用于校验
request.session['image_code'] = code_string
# 给Session设置60秒超时,即验证码有效时间只有60s
request.session.set_expiry(60)
stream = BytesIO() # 在内存中创建一个文件
img.save(stream, 'png') # 将生成的img文件写入,文件中
return HttpResponse(stream.getvalue())
发送局部请求
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理h1>
<h3>示例1h3>
<input id="btn1" type="button" class="btn btn-prima ry" value="点击" onclick="clickMe();"/>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
function clikMe(){
$.ajax({
url: '/task/ajax/', // 发送的地址
type: "get",
data: {
n1: 123,
n2: 456
},
success: function (res) {
console.log(res);
}
})
}
script>
{% endblock %}
$.ajax({
url: '/task/ajax/', // 发送的地址
type: "get",
data: {
n1: 123,
n2: 456
},
success: function (res) {
console.log(res);
}
})
{% endblock %}
from django.shortcuts import render, HttpResponse
def task_ajax(request):
print(request.GET)
return HttpResponse("成功了")
# ajax请求
path('ajax/task/', ajax.task_ajax)
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理h1>
<h3>示例1h3>
<input id="btn1" type="button" class="btn btn-prima ry" value="点击" onclick="clickMe();"/>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
function clikMe(){
$.ajax({
url: '/task/ajax/', //发送的地址
type: "get",
data: {
n1: 123,
n2: 456
},
success: function (res) {
console.log(res);
}
})
}
script>
{% endblock %}
from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def task_ajax(request):
print(request.GET)
print(request.POST)
return HttpResponse("成功了")
# ajax请求
path('ajax/task/', ajax.task_ajax)
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理h1>
<h3>示例1h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击"/>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
})
function bindBtn1Event() {
$("#btn1").click(function () { // btn1是表单id
$.ajax({
url: '/task/ajax/',
type: "post",
data: {
n1: 123,
n2: 456
},
success: function (res) {
console.log(res);
}
})
})
}
script>
{% endblock %}
返回值是json格式
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理h1>
<h3>示例1h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击"/>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: '/task/ajax/',
type: "post",
data: {
n1: 123,
n2: 456
},
dataType: "JSON", // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
success: function (res) {
console.log(res);
console.log(res.status); //通过json数据对象键获取值
console.log(res.data);
}
})
})
}
script>
{% endblock %}
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse
def task_list(request):
""" 任务列表 """
return render(request, "task_list.html")
@csrf_exempt
def task_ajax(request):
print(request.GET)
print(request.POST)
data_dict = {"status": True, 'data': [11, 22, 33, 44]}
return HttpResponse(json.dumps(data_dict)) # 将数据返回去前端页面
# return JsonResponse(data_dict) # django带的方法可以简化如上返回代码,需要导包
# ajax请求
path('ajax/task/', ajax.task_ajax)
{% extends 'layout.html' %}
{% block content %}
<hr/>
<h1>Ajax学习h1>
<h3>示例2h3>
<input type="text" id="txtUser" placeholder="姓名">
<input type="text" id="txtAge" placeholder="年龄">
<input id="btn2" type="button" class="btn btn-primary" value="点击"/>
<h3>示例3h3>
<form id="form3">
<input type="text" name="user" placeholder="姓名">
<input type="text" name="age" placeholder="年龄">
<input type="text" name="email" placeholder="邮箱">
<input type="text" name="more" placeholder="介绍">
form>
<input id="btn3" type="button" class="btn btn-primary" value="点击"/>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event2();
bindBtn1Event3();
})
function bindBtn1Event2() {
$("#btn1").click(function () {
$.ajax({
url: '/task/ajax/',
type: "post",
data: {
user: $("#textUser").val(), //jquery获取表单的值
age: $("textAge").val()
},
dataType: "JSON", // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
success: function (res) {
console.log(res);
console.log(res.status); //通过json数据对象键获取值
console.log(res.data);
}
})
})
}
function bindBtn1Event3() {
$("#btn3").click(function () {
$.ajax({
url: '/task/ajax/',
type: "post",
data: $("#from3").serialize(), //serialize()将表单所以值传入后端
dataType: "JSON", // 将后端返回数据转换成json对象格式,json数据在python中是字典(键值对)
success: function (res) {
console.log(res);
console.log(res.status); //通过json数据对象键获取值
console.log(res.data);
}
})
})
}
script>
{% endblock %}
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse
def task_list(request):
""" 任务列表 """
return render(request, "task_list.html")
@csrf_exempt
def task_ajax(request):
print(request.GET)
print(request.POST)
data_dict = {"status": True, 'data': [11, 22, 33, 44]}
return HttpResponse(json.dumps(data_dict)) # 将数据返回去前端页面
# return JsonResponse(data_dict) # django带的方法可以简化如上返回代码,需要导包
# ajax请求,注意导入ajax.py包,views文件夹中
path('ajax/task/', ajax.task_ajax),
path('ajax/list/', ajax.task_list),
知识点的回顾:
安装Djangox
pip install django
创建Django项目
>>> django-admin startproject mysite
注意:Pycharm可以创建。如果用Pycharm创建,记得settings.py中的DIR templates 删除。
创建app & 注册
>>>python manage.py startapp app01
>>>python manage.py startapp app02
>>>python manage.py startapp app03
INSTALLED_APPS = [
...
'app01.apps.App01Config'
]
注意:否则app下的models.py写类时,无法在数据库中创建表。
配置 静态文件路径 & 模板的路径(放在app目录下)。
配置数据库相关操作(MySQL)
第三方模块(django3版本)
pip install mysqlclient
自己先去MySQL创建一个数据库。
mysql -u root -p
CREATE DATABASE djangodemo3 CHARACTER SET utf8 COLLATE utf8_general_ci; utf-8后面是排列规则
配置数据库连接settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoDemo3', # 数据库名字
'USER': 'root',
'PASSWORD': '1234',
'HOST': '127.0.0.1', # 哪台机器安装了MySQL
'PORT': 3306,
}
}
在app下的models.py中编写
from django.db import models
class Admin(models.Model):
""" 管理员 """
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
def __str__(self):
return self.username
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name='标题', max_length=32)
def __str__(self):
return self.title
执行两个命令:
>>>python manange.py makemigrations
>>>python manange.py migrate
在 urls.py ,路由 ( URL 和 函数的对应关系)。
在views.py,视图函数,编写业务逻辑。
templates目录,编写HTML模板(含有模板语法、继承、{% static 'xx'%}
)
ModelForm & Form组件,在我们开发增删改查功能。
Cookie和Session,用户登录信息保存起来。
中间件,基于中间件实现用户认证 ,基于:process_request
。
ORM操作
models.User.objects.filter(id="xxx")
models.User.objects.filter(id="xxx").order_by("-id")
分页组件。