安装 pip install django
,这是命令行安装 pycharm中直接在setting里安装
创建项目
先进入一个指定的目录,在执行命令: 这边是python的安装目录下,在安装django后会有一个django-admin.exe
左上角点File再点New Project: 选第二个django,然后选择项目创建在哪儿即可
pycharm创建出来的会多一个template目录 settings文件开头也有点不同:
默认文件介绍 默认项目里的文件有以下6个: template是空的,暂时不管
接受网络信息的文件,头部请求,不需要动
也是接受网络请求,异步请求,不需要动
项目配置文件
路由设置
app的创建和说明 每一个大项目有很多小功能,这些小功能拆分开来就是app了 通过manage.py去创建app 在pycharm的终端输入:python manage.py startapp app01
: 这边假如报错: 在settings.py里添加:
app里会自动创建一些文件:
这个固定不动
单元测试的,固定不动
固定不动,数据库字段变更
django默认提供了amdin后台管理,固定不动
重要内容,和函数有关
重要内容,对数据库进行操作
启动运行django 首先确保app注册,上面只是创建了app,并没注册,找到settings.py: 在末尾加上一句就注册成功了 接下来编写url和视图函数对应关系,在urls.py去编写: 这一句话是默认的,我们注释掉 表示如果用户URL+/index
之后就会请求后面的函数 导入app01下的views.py后,访问index就是去请求views.py里面的index函数
1 2 3 4 5 6 7 from django.shortcuts import render,HttpResponsedef index (request): return HttpResponse ("欢迎使用" )
index函数就这样做好了 接下来启动django程序: 可以通过命令行 或者pycharm启动 命令行:python manage.py runserver
pycharm: 点击运行文件即可 点Django进去可以配置,8000端口 这样就直接给我们返回了函数
模板和静态文件 如果我们想要返回一个字符串用httpresponse即可,但是如果我们想要给用户返回一个html文件,那咱们就得用template目录了 大概就如上 在template文件夹下放这个html文件 这边查找到html文件的顺序为,如果app01注册了,那就在app01目录下去找template,如果没有找到就在下一个app里的template找,如果都找不到就在上一级目录里的template去找 这是因为上面的DIRS的效果,如果删除了那就是去根据app的注册顺序去查找!
静态文件: 静态文件指的就是js,css,图片这些,这些文件也有讲究,不能乱放 静态文件要放在static文件夹下! 这是在settings的这里设置的 这个static文件夹放在app或者是外面都可以,最好还是app下面,更整洁 接下来我们把js和css等等都创建: 用如上的方式去引入文件
Django模板语法 引用变量 新建一个tpl函数和tpl.html 我们在函数内定义了一个变量,那我们怎么把这个变量放在页面里呢? 后面跟一个字典,然后再html中: 2个花括号就是引用: 这里面也可以放列表,字典
语句 在{%%}
中可以写判断和循环语句
循环语句 endfor表示结束循环
列表内套用字典形式大概就像n4.0.name
或者是n4.0.itmes
之类的,都一样用的
条件语句 1 2 3 4 5 {% if n1 == "Boogipop" %} <h3>嘻嘻</h3> {% else %} <h3>嘿嘿</h3> {% endif %}
请求和响应 请求 先定义一个函数request来演示:
1 2 3 4 5 6 7 8 9 10 def request (request ): print (request.method) print (request.GET) print (request.POST) return HttpResponse("返回数据" )
request.method: request.GET: request.POST: 目前没办法,因为有些东西没设置,所以没法演示,和GET一样的其实是
响应 这边演示一个重定向: 这里要导入一下redirect模块: 访问之后就过来了,这边处理方式为: 浏览器->Django,然后让浏览器自己去访问百度: 为第二种方式
小案例:用户登录 概况如下: 代码分别为:
1 2 3 4 5 6 7 8 9 10 11 12 def login (request ): if request.method=="GET" : return render(request,"login.html" ) else : username=request.POST.get("user" ) password=request.POST.get("pwd" ) print ("用户名:%s,密码:%s" %(username,password)) if username=="root" and password=="root" : return redirect("http://43.140.251.169/" ) else : return render(request,'login.html' ,{"error_msg" :"用户名或密码错误" })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 用户登入</title > </head > <body > <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: blue" > {{ error_msg }}</span > </form > </body > </html >
这样就完成了一个简单的登录页面: 后台会显示输入了什么:
ORM-连接mysql的模块 orm比pymysql更加的简洁 先要安装mysqlclient: 由于我是python3.7然后pycharm又是2020版本的,所以安装不到最新版,好像是这么一回事,安装了一个2.1版本,安装方法也有点不同 参考:https://blog.csdn.net/cn_1937/article/details/81533544 安装包:https://pypi.org/project/mysqlclient/2.1.0/#files 安装完后即可,用pip install 下载的文件名
ORM-创建数据库 orm可以帮助我们做两件事情
创建,修改,删除数据库中的表【无法创建数据库】
操作表中的数据
1.自己创建数据库 现在windows下载一下mysql:
遇到的问题:
1 2 3 4 mysqld.exe --initialize --console // 初始化 mysqld.exe install mysql // 安装服务 net start mysql // 启动服务 net stop mysql // 停止命令
create databse boogipop
:
orm-连接mysql 在settings如下设置:
1 2 3 4 5 6 7 8 9 10 11 DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'NAME' : 'boogipop' , 'USER' :'root' , 'PASSWORD' :'jiayou357753' , 'HOST' :'127.0.0.1' , 'PORT' :3306 , } }
orm-操作表 接下来的操作就是在models.py里了,之前也说了这是对数据库进行操作的py文件
1 2 3 4 5 from django.db import modelsclass Userinfo (models.Model): name=models.CharField(max_length=32 ) password=models.CharField(max_length=64 ) age=models.IntegerField()
如果需要django帮我们去创建表,还需要2个命令: 终端输入:python manage.py makemigrations
;python manage.py migrate
这边遇到了一些问题,如果出现就说明你的app目录下没有migration文件夹,并且里面要有init文件,我之前不小心弄出来了所以报错了!!!! 在mysql中可以看到出来了很多表,其中就包含了我们的app01_userinfo
为什么会有其他的很多表呢?因为我们注册了很多app: django在注册表的时候,会去每个app的models.py文件下去查找,其他app里也有models.py,所以后面多出来了一些表
增删改查
1 2 #新建数据 相当于 insert into app01_department (title) values("娱乐部" ) Department.objects.create( title="娱乐部" )
可以一次性给多个字段添加数据,后面加逗号分割 接下来我们来测试orm,首先添加一下orm路由和函数: 然后在views.py导入models.py: 之后就用models.Userinfo.xxxx
去进行增删改查操作 再访问网址: 这里要注意一下,一定要把department表注册了哦 然后在数据库中查看: 成功添加数据
删除数据
1 2 3 models.Userinfo.objects.filter(id=2 ).delete()#删除id=2 的用户 models.Userinfo.objects.filter(id=3 ).delete()#删除id=3 的用户 # models.Userinfo.objects.all().delete() #删除所有用户
获取数据
测试的表如下:
1 print(models.Userinfo.objects.all()) #获取userinfo表里的所有数据
说明里面有两行数据,每一行都是一个对象,如何取数据呢? 用循环语句去获取 如下:
1 2 3 4 data=models.Userinfo.objects.all () for i in data: print (i.id ,i.age,i.name,i.password)
还可以用filter筛选:
1 data=models.Userinfo.objects.filter (id =1 )
得到的也是一个对象,是第一行数据
1 2 3 4 5 a=models.Userinfo.objects.filter (id =1 ).first print (a.id )a=models.Userinfo.objects.filter (id =1 )[0 ] print (a.id )
修改数据1 models.Userinfo.objects.filter (id =4 ).update(name="heart" )
ORM案例-用户管理 功能1-展示用户列表 新建一个infolist函数并添加路由 再写一个html文件如下: 源码我最后放 可以看到成功显示!
功能2-添加用户 是自己手打的哦
1 2 3 4 5 6 7 8 9 def adduser (request ): if request.method=='GET' : return render(request,"user_add.html" ) if request.method=='POST' : username=request.POST.get("user" ) password=request.POST.get("pwd" ) age=request.POST.get("age" ) models.Userinfo.objects.create(name=username,password=password,age=age) return HttpResponse("添加成功!!" )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 添加用户</title > </head > <body > <h1 > 添加用户</h1 > <form method ="post" action ="/info/add/" > {% csrf_token %} <input type ="text" name ="user" placeholder ="用户名" > <input type ="password" name ="pwd" placeholder ="密码" > <input type ="text" name ="age" placeholder ="年龄" > <input type ="submit" value ="提交" > </form > </body > </html >
进一步美化:
功能3-删除用户 1 2 3 4 def deluser (request ): uid=request.GET.get("uid" ) models.Userinfo.objects.filter (id =uid).delete() return redirect("/info/list" )
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 用户列表</title > </head > <body > <h1 > Information</h1 > <a href ="http://localhost:8000/info/add" > 添加用户</a > <table border ="1" > <thead > <tr > <th > ID</th > <th > 姓名</th > <th > 密码</th > <th > 年龄</th > <th > 操作</th > </tr > </thead > <tbody > {% for obj in data_list %} <tr > <td > {{ obj.id }}</td > <td > {{ obj.name }}</td > <td > "{{ obj.password }}"</td > <td > {{ obj.age }}</td > <td > <a href ="http://127.0.0.1:8000/info/del/?uid={{ obj.id }}" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > </body > </html >
至此就结束了:
员工管理系统 创建项目和app 点run manage.py task: 输入startapp app01
,在这之前也需要settings里improt os
: 创建成功QWQ 别忘了注册
表结构的创建 创建表的信息如上,foreignkey表示depart_id只可以在department中的id的值范围里取,另外在数据库中foreignkey会在depart后面自动加上一个_id,最后是depart_id 为什么Department里为什么没有id字段呢?这是因为django会自己帮你创建一个
但这个还不是最终的表,假如我们删除一个部门,那底下的用户是不是也该删除呢? 要在ForeignKey后面加: 另外如果我们不想删除这些部门的人,我们把他的部门ID滞空,可以这样写:
再加上性别,最终的代码为:
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 from django.db import modelsclass Department (models.Model): '''部门表''' title=models.CharField(verbose_name="标题" ,max_length=64 ) class UserInfo (models.Model): '''员工表''' name=models.CharField(verbose_name="名字" ,max_length=32 ) 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' ,null=True ,blank=True ,on_delete=models.SET_NULL) gender_choice=( (1 ,"男" ), (2 ,"女" ) ) gender=models.SmallIntegerField(verbose_name="性别" ,choices=gender_choice)
MYSQL生成数据库 create database OA
: 再用django去创建一下表: 先修改配置文件连接mysql: 再运行指令即可:
部门列表展示 先加入一些静态文件 然后准备个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 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 {% 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/css/bootstrap.min.css' %}" > <style > .navbar { border-radius : 0 ; } </style > </head > <body > <nav class ="navbar navbar-default" > <div class ="container" > <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 > <div class ="collapse navbar-collapse" id ="bs-example-navbar-collapse-1" > <ul class ="nav navbar-nav" > <li > <a href ="/depart/list/" > 部门管理</a > </li > <li > <a href ="/user/list/" > 用户管理</a > </li > <li > <a href ="#" > Link</a > </li > </ul > <ul class ="nav navbar-nav navbar-right" > <li > <a href ="#" > 登录</a > </li > <li class ="dropdown" > <a href ="#" class ="dropdown-toggle" data-toggle ="dropdown" role ="button" aria-haspopup ="true" aria-expanded ="false" > Boogipop <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 > </div > </nav > <div > <div style ="margin-bottom: 10px;" > <a class ="btn btn-success" href ="#" > <span class ="glyphicon glyphicon-plus" 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 > ID</th > <th > 部门名称</th > <th > 操作</th > </tr > </thead > <tbody > {% for data in Infolist%} <tr > <td > {{ data.id }}</td > <td > {{ data.title }}</td > <td > <a class ="btn btn-primary btn-xs" > 编辑</a > <a class ="btn btn-danger btn-xs" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > </div > </div > <script src ="{% static 'js/jquery-3.6.0.min.js' %}" > </script > <script src ="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}" > </script > </body > </html >
最后效果如下: 还挺不错的哈
数据库数据 insert into app_department(title) values("心理部");
insert into app01_department(title) values("CTF部");
然后在html页面添加模板语句: 成功显示
新建部门 事先说好,这些模板都是在https://v3.bootcss.com/css/#forms-horizontal 直接拿的 先添加一条a标签,跳转到添加页面 添加路由 添加函数 然后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 64 65 66 67 68 69 70 71 72 73 {% 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/css/bootstrap.min.css' %}" > <style > .navbar { border-radius : 0 ; } </style > </head > <body > <nav class ="navbar navbar-default" > <div class ="container" > <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 > <div class ="collapse navbar-collapse" id ="bs-example-navbar-collapse-1" > <ul class ="nav navbar-nav" > <li > <a href ="/depart/list/" > 部门管理</a > </li > <li > <a href ="/user/list/" > 用户管理</a > </li > <li > <a href ="#" > Link</a > </li > </ul > <ul class ="nav navbar-nav navbar-right" > <li > <a href ="#" > 登录</a > </li > <li class ="dropdown" > <a href ="#" class ="dropdown-toggle" data-toggle ="dropdown" role ="button" aria-haspopup ="true" aria-expanded ="false" > Boogipop <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 > </div > </nav > <div > <div class ="container" > <div class ="panel panel-default" > <div class ="panel-heading" > <h3 class ="panel-title" > 新建部门</h3 > </div > <div class ="panel-body" > <form > <div class ="form-group" > <label > 标题</label > <input type ="text" class ="form-control" placeholder ="标题" name ="title" > </div > <button type ="submit" class ="btn btn-primary" > 提交</button > </form > </div > </div > </div > </div > <script src ="{% static 'js/jquery-3.6.0.min.js' %}" > </script > <script src ="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}" > </script > </body > </html >
部门添加 设置一下提交方式,然后再改一下函数: 测试: 成功添加!
删除 添加函数跟路由
1 <a class ="btn btn-primary btn-xs" href ="http://127.0.0.1:8000/depart/del/?uid={{ data.id }}" > 删除</a >
修改HTML代码 成功删除咯
编辑 添加路由和函数,以及html页面 接下来获取ID咱们就用一种不一样的方式: 酱紫修改就是一种船新的方式,可以达到上面删除界面中同样的效果! 这样就可以拿到对应的数据: 再往下这么写就可以出现默认值
1 <input type ="text" class ="form-control" placeholder ="标题" name ="title" value ="{{ data.title }}" >
默认值已经出来了~ 接下来要做的事情就是更新了,对提交的数据做出处理 表单里不写action默认提交到当前页面
1 2 3 if request.method=='POST' : obj=models.Department.objects.filter (id =nid).update(title=request.POST.get("title" )) return redirect('/depart/list' )
这样就完成了修改功能 yaho~成功拉
模板的继承 在django中,我们是可以html模板的,具体操作如何实现呢,接下来就来演示 先准备layout.html,内容就从depart_list.html直接复制过来: 重点看这里,这边是div分的区域,在这里可以进行继承: 修改为这样,其中content是们自定义的,然后再修改depart_list.html: layout也就是depart_list的模板,是父类,这样界面不会改变: 同理也可以修改depart_edit页面: 新建部门页面也是同理啊~ 这很方便qwq!!!另外注意了,可以定义多个模板点,也就是多个block,修改content为其他的东西即可block content
,block content1
这样就行
用户列表 老方法先添加路由,函数,html: 再编辑一下html界面: 大致为这样: 然后修改一下部门列表里的超链接: 用户数据库信息如下: 我们先手动插入几条数据:
1 2 3 insert into app01_userinfo values(1 ,"boogipop" ,"root" ,15 ,"100.00" ,"2018-10-15" ,2 ,1 ) insert into app01_userinfo values(2 ,"kino" ,"root" ,14 ,"120.00" ,"2018-12-15" ,2 ,2 ) insert into app01_userinfo values(3 ,"Alex" ,"root" ,18 ,"130.00" ,"2018-11-15" ,1 ,5 )
目前数据库就如上
1 2 3 4 5 6 userinfo=models.UserInfo.objects.all () for data in userinfo: print (data.id ,data.name,data.password,data.age,data.account,data.create_time.strftime("%Y-%m-%d" ),data.get_gender_display(),data.depart.title)
函数如上: 数据都会打印出来,这样就获取了数据,接下来就展示出来 这样写会有一些报错,因为那是python的函数,我们再html写会报错 django中默认会给你加括号,所以不需要括号,如果括号里有参数就用|
隔开,data就表示参数
这样界面就出来了
用户添加(原始方法) 路由! html!: user_list里的href!: 最后的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 {% extends 'layout.html' %} {% block content %} <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 ="名称" > </div > <div class ="form-group" > <label > 密码</label > <input type ="text" class ="form-control" placeholder ="密码" > </div > <div class ="form-group" > <label > 年龄</label > <input type ="text" class ="form-control" placeholder ="年龄" > </div > <div class ="form-group" > <label > 账户余额</label > <input type ="text" class ="form-control" placeholder ="账户余额" > </div > <div class ="form-group" > <label > 入职时间</label > <input type ="text" class ="form-control" placeholder ="入职时间" > </div > <div class ="form-group" > <label > 性别</label > <select class ="form-control" > {% for choice in gender %} <option value ="{{ choice.0 }}" > {{choice.1}}</option > {% endfor %} </select > </div > <div class ="form-group" > <label > 部门</label > <select class ="form-control" > {% for choice in departid %} <option value ="{{ choice.id }}" > {{ choice.title }}</option > {% endfor %} </select > </div > <button type ="submit" class ="btn btn-primary" > 提交</button > </form > </div > </div > {% endblock %}
函数为: 这样就可以把所有用户展示出
假如我们按照如上操作获取参数,那么会有以下几个缺点
参数校验繁琐 错误提示繁琐 页面每个字段都需要重新写一遍 如果有关联的数据,还要先取出来再循环得出,麻烦
form可以在上面的基础上进一步的去优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from django import formsclass MyForm (forms.Form): user = forms.CharField(widget=forms.Input) pwd = forms.CharFiled(widget=forms.Input) email = forms.CharFiled(widget=forms.Input) account = forms.CharFiled(widget=forms.Input) create_time = forms.CharFiled(widget=forms.Input) depart = forms.CharFiled(widget=forms.Input) gender = forms.CharFiled(widget=forms.Input) def user_add (request ): if request.method == "GET" : form = MyForm() return render(request, 'user_add.html' ,{"form" :form})
views.py内容如上,我们只需在函数上边儿定义一个类,继承Form类即可 然后user,pwd这些怎么利用呢?HTML界面中这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <form method ="post" > {% for field in form%} {{ field }} {% endfor %} </form > <form method ="post" > {{ form.user }} {{ form.pwd }} {{ form.email }} </form >
这就相当于我们的input标签!是不是很赞
可以理解为Form组件的升级版,这样会更加轻松:
1 2 3 4 5 6 7 8 9 10 11 from django import formsclass MyForm (forms.ModelForm): class Meta : model = UserInfo fields = ["name" ,"password" ,"age" ] def user_add (request ): if request.method == "GET" : form = MyForm() return render(request, 'user_add.html' ,{"form" :form})
1 2 3 4 5 6 <form method="post" > {% for field in form%} {{ field }} {% endfor %} <!-- <input type ="text" placeholder="姓名" name="user" /> --> </form>
是不是更简洁了
form.name.label
表示取一开始的verbose_name 实操如下:
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>Title</title> </head> <body> {% for field in form %} {{ field.label }}: {{ field }} {% endfor %} </body> </html>
1 2 3 4 5 6 7 8 from django import formsclass Userform (forms.ModelForm): class Meta : model=models.UserInfo fields=["name" ,"password" ,"age" ,"account" ,"create_time" ,"gender" ,"depart" ] def user_modelform_add (request ): form=Userform() return render(request, 'user_modelform_add.html' ,{"form" :form})
1 2 3 4 5 6 from django.db import modelsclass Department (models.Model): '''部门表''' def __str__ (self ): return self.title title=models.CharField(verbose_name="标题" ,max_length=64 )
假如不使用str魔术方法的结果为: 这个原因是为什么应该懂吧,一行数据就是对象,用str魔术方法,让他print的时候输出title,最后效果如下: 是不是很方便呢?
原理弄懂了就把他逐步完善一下: 界面样式为: 感觉有点丑啊 定义widgets加载css: 这样还是很麻烦: 这里输出的name和field应该都不陌生吧 所以这样表示在field中都加上一个class 最后再加上一个placeholder在class后边儿: 继续完善
1 2 3 4 5 6 7 8 9 10 11 12 13 def user_modelform_add (request ): if request.method=='GET' : form=Userform() return render(request, 'user_modelform_add.html' ,{"form" :form}) form=Userform(data=request.POST) if form.is_valid(): form.save() return redirect('/user/list/' ) else : print (form.errors)
这里就是参数校验环节 成功完成添加用户
设置错误信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django import formsclass Userform (forms.ModelForm): name=forms.CharField(min_length=3 ,label='名称' ) password=forms.CharField(min_length=8 ,label='密码' ) class Meta : model=models.UserInfo fields=["name" ,"password" ,"age" ,"account" ,"create_time" ,"gender" ,"depart" ] def __init__ (self,*args,**kwargs ): super ().__init__(*args,**kwargs) for name,field in self.fields.items(): field.widget.attrs={"class" :"form-control" ,"placeholder" :field.label}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def user_modelform_add (request ): if request.method=='GET' : form=Userform() return render(request, 'user_modelform_add.html' ,{"form" :form}) form=Userform(data=request.POST) if form.is_valid(): form.save() return redirect('/user/list/' ) else : return render(request,'user_modelform_add.html' ,{"form" :form})
filed.errors.0
表示错误信息的第一条,如果不加0结果会很乱 然后novalidate
是不需要前端给我们验证参数是否为空,这是为了体验错误信息嘛
然后最后不顺眼的地方就是错误信息是英文,我们如何改为中文呢? 改为: 成功!
用户编辑和删除 咳咳,从现在开始就简短的写了,都很熟练了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def user_edit (request,nid ): row_obj = models.UserInfo.objects.filter (id =nid).first() if request.method=='GET' : form = Userform(instance=row_obj) return render(request,'user_edit.html' ,{"form" :form}) form=Userform(data=request.POST,instance=row_obj) if form.is_valid(): form.save() return redirect('/user/list/' ) return render(request,'user_edit.html' ,{"form" :form}) def user_del (request,nid ): models.UserInfo.objects.filter (id =nid).delete() return redirect('/user/list/' )
1 2 path('user/<int:nid>/edit/' ,views.user_edit), path('user/<int:nid>/del/' ,views.user_del),
页面就是直接抄user_add的,改一点文字就可以
靓号管理-查看 也是直接上路由,HTML,函数了
1 2 3 4 def pretty_list (request ): prettynum=models.pretty.objects.all ().order_by('-level' ) return render(request,'pretty_list.html' ,{"info" :prettynum})
1 path('pretty/list/' ,views.pretty_list),
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 {% extends 'layout.html' %} {% block content %} <div style ="margin-bottom: 10px;" > <a class ="btn btn-success" href ="/user/add/" target ="_blank" > <span class ="glyphicon glyphicon-plus" 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 > ID</th > <th > 号码</th > <th > 价格</th > <th > 级别</th > <th > 状态</th > <th > 操作</th > </tr > </thead > <tbody > {% for data in info %} <tr > <td > {{ data.id }}</td > <td > {{ data.mobile }}</td > <td > {{ data.price }}</td > <td > {{ data.get_level_display }}</td > <td > {{ data.get_status_display }}</td > <td > <a class ="btn btn-primary btn-xs" href ="/user/{{ data.id }}/edit/" > 编辑</a > <a class ="btn btn-danger btn-xs" href ="/user/{{ data.id }}/del/" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > </div > {% endblock %}
修改了一点
新建靓号
列表点击跳转:/pretty/add/
URL
ModelForm类
1 2 3 4 from django import forms class PrettyModelForm(forms.ModelForm): ...
函数
实例化类的对象
通过render将对象传入到HTML中。
模板的循环展示所有的字段。
点击提交
代码分别如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from django.core.validators import RegexValidatorclass Prettyadd (forms.ModelForm): mobile=forms.CharField(label='号码' ,validators=[RegexValidator(r'^158[0-9]+$' ,'号码必须以158开头,并且至少4位数' )]) class Meta : model=models.pretty fields='__all__' def __init__ (self,*args,**kwargs ): super ().__init__(*args,**kwargs) for name,field in self.fields.items(): field.widget.attrs={"class" :"form-control" ,"placeholder" :field.label} def pretty_add (request ): if request.method=='GET' : form=Prettyadd() return render(request,'pretty_add.html' ,{"form" :form}) form=Prettyadd(data=request.POST) if form.is_valid(): form.save() return redirect('/pretty/list/' ) else : return render(request,'pretty_add.html' ,{"form" :form})
__all__表示所有子弹
RegexValidator可以正则匹配
exclude为除开某些字段外
正则限制了号码的输入
还有一种方法二 self.cleaned_data就是用户输入的,我们加一个if对他判断即可
1 2 from django.core.exceptions import ValidationErrorraise ValidationError('msg' )就是抛出msg错误信息
靓号编辑和删除
列表页面:/pretty/数字/edit/
URL
函数
根据ID获取当前编辑的对象
ModelForm配合,默认显示数据。
提交修改。
不允许手机号重复
不允许重复: 添加函数:
1 2 3 4 5 6 7 queryset = models.PrettyNum.objects.filter (mobile="1888888888" ) obj = models.PrettyNum.objects.filter (mobile="1888888888" ).first() exists = models.PrettyNum.objects.filter (mobile="1888888888" ).exists()
编辑函数(手机号不能存在):
1 2 3 4 排除自己以外,其他的数据是否手机号是否重复? models.PrettyNum.objects.filter (mobile="1888888888" ).exclude(id =2 )
mobile=forms.CharField(disabled=True,label='号码')
: 此条语句一出,就说明某个数据不能变动:
最后就简简单单的上个源码拉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Prettyadd (forms.ModelForm): mobile=forms.CharField(label='号码' ,validators=[RegexValidator(r'^158[0-9]+$' ,'号码必须以158开头,并且至少4位数' )]) class Meta : model=models.pretty fields='__all__' def __init__ (self,*args,**kwargs ): super ().__init__(*args,**kwargs) for name,field in self.fields.items(): field.widget.attrs={"class" :"form-control" ,"placeholder" :field.label} def clean_mobile (self ): '''检测手机号是否重复 ''' number=self.cleaned_data['mobile' ] exists=models.pretty.objects.filter (mobile=number).exists() if exists: raise ValidationError('手机号已存在' ) return number
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 class Prettyedit (forms.ModelForm): mobile=forms.CharField(label='号码' ,validators=[RegexValidator(r'^158[0-9]+$' ,'号码必须以158开头,并且至少4位数' )]) class Meta : model=models.pretty fields=['mobile' ,'price' ,'level' ,'status' ] def __init__ (self,*args,**kwargs ): super ().__init__(*args,**kwargs) for name,field in self.fields.items(): field.widget.attrs={"class" :"form-control" ,"placeholder" :field.label} def clean_mobile (self ): '''检测手机号是否重复 ''' number = self.cleaned_data['mobile' ] exists = models.pretty.objects.filter (mobile=number).exclude(id =self.instance.pk).exists() if exists: raise ValidationError('手机号已存在' ) return number def pretty_edit (request,nid ): row_obj = models.pretty.objects.filter (id =nid).first() if request.method == 'GET' : form = Prettyedit(instance=row_obj) return render(request, 'pretty_edit.html' , {"form" : form}) form = Prettyedit(data=request.POST, instance=row_obj) if form.is_valid(): form.save() return redirect('/pretty/list/' ) return render(request, 'pretty_edit.html' , {"form" : form}) def pretty_del (request,nid ): models.pretty.objects.filter (id =nid).delete() return redirect('/pretty/list/' )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 {% extends 'layout.html' %} {% block content %} <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 > {% endblock %}
1 2 path('pretty/<int:nid > /edit/',views.pretty_edit), path('pretty/<int:nid > /del/',views.pretty_del),
搜索 先介绍一下其实filter还可以传入字典:
1 2 3 dict ={"id" :1 ,"name" :"kino" } models.pretty.objects.filter (**dict )
这样也可以进行筛选 filter还有其他用法,还有其他的筛选用法如下:
1 2 3 4 5 6 7 8 models.pretty.objects.filter (id =1 ) models.pretty.objects.filter (id__gt=12 ) models.pretty.objects.filter (id__gte=12 ) models.pretty.objects.filter (id__lt=12 ) models.pretty.objects.filter (id__lte=12 ) models.pretty.objects.filter (mobile__startwith="158" ) models.pretty.objects.filter (mobile__endwith="666" ) models.pretty.objects.filter (mobile__contains="158666" )
这样可以来获取我们URL输入的,再检查,比如输入?q=0
: 这里的if value并不是指的是value为0就不通过判断,而是判断是否为空 给源码了:
1 2 3 4 5 6 7 8 9 def pretty_list (request ): data={} value=request.GET.get('q' ,"" ) if value: data["mobile__contains" ]=value res=models.pretty.objects.filter (**data) prettynum=models.pretty.objects.filter (**data).order_by('-level' ) return render(request,'pretty_list.html' ,{"info" :prettynum,"search" :value})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div style ="margin-bottom: 10px;" class ="clearfix" > <a class ="btn btn-success" href ="/pretty/add/" target ="_blank" > <span class ="glyphicon glyphicon-plus" aria-hidden ="true" > </span > 新建靓号 </a > <div style ="float: right;width: 300px;" > <form method ="get" > <div class ="input-group" > <input type ="text" class ="form-control" name ="q" placeholder ="Search for..." value ="{{ search }}" > <span class ="input-group-btn" > <button class ="btn btn-default" type ="submit" > <span class ="glyphicon glyphicon-search" aria-hidden ="true" > </span > </button > </span > </div > </form > </div > </div >
也不难
分页(原理) 诶,四十多分钟的教学,看的头皮发麻,做开发的真难 首先先来讲一下整体思路吧,思路:
思路说完了就来实操吧,你已经会打水泥了来造房子吧! 这里比较繁琐就一步步介绍了,首先先添加300条数据来做测试: 在这之后:
1 2 3 4 5 6 page=int (request.GET.get("page" ,1 )) step=10 start=(page-1 )*step end=page*step prettynum=models.pretty.objects.filter (**data).order_by('-level' )[start:end]
和思路里介绍的一样,那接下来就是设置分页框了,去bootstrap拿下来
1 2 3 4 5 <nav aria-label ="Page navigation" > <ul class ="pagination" > {{ change }} </ul > </nav >
再设置一下总页数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 page=int (request.GET.get("page" ,1 )) step=10 start=(page-1 )*step end=page*step page_str_list=[] total_list=models.pretty.objects.all ().count() total_page,div=divmod (total_list,step) if div!=0 : total_page=total_page+1 for i in range (1 ,total_page+1 ): ele='<li><a href="?page={}">{}</a></li>' .format (i,i) page_str_list.append(ele) page_string="" .join(page_str_list) page_string=mark_safe(page_string)
效果就如下了 下面是对分页效果的优化,展示11个页码,然后临界点问题,不能有负数 也直接拿源码了
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 def pretty_list (request ): data={} value=request.GET.get('q' ,"" ) if value: data["mobile__contains" ]=value res=models.pretty.objects.filter (**data) page=int (request.GET.get("page" ,1 )) step=10 start=(page-1 )*step end=page*step page_str_list=[] total_list=models.pretty.objects.all ().count() total_page,div=divmod (total_list,step) if div!=0 : total_page=total_page+1 plus=5 start_page=page-plus if (page-plus)>1 else 1 end_page=page+plus+1 if (page+plus+1 )<total_page else total_page up='<li><a href="?page=1">首页</a></li>' page_str_list.append(up) if page>1 : prev='<li><a href="?page={}">上一页</a></li>' .format (page-1 ) else : prev ='<li><a href="?page=1">上一页</a></li>' page_str_list.append(prev) for i in range (start_page,end_page+1 ): if i==page: ele='<li class="active"><a href="?page={}">{}</a></li>' .format (i,i) else : ele='<li><a href="?page={}">{}</a></li>' .format (i,i) page_str_list.append(ele) if page < total_page: suffix = '<li><a href="?page={}">下一页</a></li>' .format (page + 1 ) else : suffix = '<li><a href="?page={}">下一页</a></li>' .format (total_page) page_str_list.append(suffix) page_string = "" .join(page_str_list) page_string=mark_safe(page_string) prettynum=models.pretty.objects.filter (**data).order_by('-level' )[start:end]
分页组件 先把上题的再完善一下做个跳转
做好基本分页后,会发现点了另一页之后参数p就会被page覆盖,这2个参数是不能同时存在的QWQ 首先要知道一些函数:
1 2 3 4 5 test=request.GET test._mutable=True test.setlist('xx' ,[11 ,12 ]) print (test)print (test.urlencode())
通常来说request.GET是获得GET所有的参数,是个字典,之后是不能使用setlist去添加参数的,要用_mutable,开启这个选项之后就可以添加 接下来直接上源码,可能有点绕,我绕了好久啊。。。 真的发现开发其实挺难的
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 ''' 分页组件 ''' from django.utils.safestring import mark_safe class jump (object ): def __init__ (self,request,queryset,step=10 ,page_param="page" ,plus=5 ): query_dict=request.GET query_dict._mutable=True self.query_dict=query_dict page=request.GET.get(page_param,"1" ) if page.isdecimal(): page=int (page) else : page=1 self.step=step self.page=page self.page_param=page_param self.start = (page - 1 ) * step self.end = page * step self.queryset=queryset[self.start:self.end] self.plus=plus total_list=queryset.count() print (queryset) total_page,div=divmod (total_list,step) if div: self.total_page=total_page+1 else : self.total_page=total_page print (self.total_page, total_list,div) def HTMl (self ): page_str_list = [] start_page = self.page - self.plus if (self.page - self.plus) > 1 else 1 end_page = self.page + self.plus + 1 if (self.page + self.plus + 1 ) < self.total_page else self.total_page print (end_page) self.query_dict.setlist(self.page_param,[1 ]) up = '<li><a href="?{}">首页</a></li>' .format (self.query_dict.urlencode()) page_str_list.append(up) if self.page > 1 : self.query_dict.setlist(self.page_param, [self.page - 1 ]) prev = '<li><a href="?{}">上一页</a></li>' .format (self.query_dict.urlencode()) else : self.query_dict.setlist(self.page_param, [1 ]) prev = '<li><a href="?{}">上一页</a></li>' .format (self.query_dict.urlencode()) page_str_list.append(prev) print (self.page,self.plus,start_page,end_page) for i in range (start_page, end_page + 1 ): if i == self.page: self.query_dict.setlist(self.page_param,[i]) ele = '<li class="active"><a href="?{}">{}</a></li>' .format (self.query_dict.urlencode(), i) else : self.query_dict.setlist(self.page_param,[i]) ele = '<li><a href="?{}">{}</a></li>' .format (self.query_dict.urlencode(), i) page_str_list.append(ele) if self.page < self.total_page: self.query_dict.setlist(self.page_param, [self.page+1 ]) suffix = '<li><a href="?{}">下一页</a></li>' .format (self.query_dict.urlencode()) else : self.query_dict.setlist(self.page_param, [self.total_page]) suffix = '<li><a href="?{}">下一页</a></li>' .format (self.query_dict.urlencode()) page_str_list.append(suffix) jump = ''' <li> <form method="get" style="float: left;margin-left: -1px"> <div class="input-group" style="width: 200px"> <input type="text" name="page" class="form-control" placeholder="页码"> <span class="input-group-btn"> <button class="btn btn-default" tyoe="submit">跳转</button> </span> </div> </form> </li> ''' page_str_list.append(jump) page_string = "" .join(page_str_list) page_string = mark_safe(page_string) return page_string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def pretty_list (request ): data={} value=request.GET.get('q' ,"" ) if value: data["mobile__contains" ]=value res=models.pretty.objects.filter (**data) prettynum=models.pretty.objects.filter (**data).order_by('-level' ) jump_obj=jump(request,prettynum,step=15 ) queryset=jump_obj.queryset page_string=jump_obj.HTMl() content={ "info" :queryset, "search" :value, "change" :page_string } return render(request,'pretty_list.html' ,content)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def user_list (request ): '''用户列表''' userinfo=models.UserInfo.objects.all () page_object=jump(request,userinfo,5 ) content={ "userinfo" :page_object.queryset, "page_string" :page_object.HTMl() } return render(request,'user_list.html' ,content)
用户页表的html: 最终展示:
时间挂件 首先先引入时间插件 这东西去官网下载吧,之后再更改一下模板文件和用户添加文件就好了: 模板文件多添加2个block分别来放css和js文件,js的block一定放下面,因为有定义函数 用户编辑页面:
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 {% extends 'layout.html' %} {% load static %} {% block css %} <link rel ="stylesheet" href ="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}" > {% endblock %} {% block content %} <div class ="panel panel-default" > <div class ="panel-heading" > <h3 class ="panel-title" > ModelForm新建用户</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 > {% endblock %} {% block js %} <script src ="{% static 'plugins/bootstrap-datepicker/js/bootstrap-datepicker.js' %}" > </script > <script src ="{% static 'plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}" > </script > <script > $(function ( ) { $('#id_create_time' ).datepicker ({ format : 'yyyy-mm-dd' , startDate : '0' , language : "zh-CN" , autoclose : true }); }) </script > {% endblock %}
这里的id_create_time
是django的modelform模板自己默认的值:
Bootstrap样式父类 之前我们给输入框添加属性是通过添加插件:
ModelForm可以把我们生成HTML标签:
1 2 3 4 5 6 class UserModelForm (forms.ModelForm): class Meta : model = models.UserInfo fields = ["name" , "password" ,] form = UserModelForm()
定义插件:
1 2 3 4 5 6 7 8 9 class UserModelForm (forms.ModelForm): class Meta : model = models.UserInfo fields = ["name" , "password" ,] widgets = { "name" : forms.TextInput(attrs={"class" : "form-control" }), "password" : forms.PasswordInput(attrs={"class" : "form-control" }), "age" : forms.TextInput(attrs={"class" : "form-control" }), }
1 2 3 4 5 6 7 8 9 10 11 class UserModelForm (forms.ModelForm): name = forms.CharField( min_length=3 , label="用户名" , widget=forms.TextInput(attrs={"class" : "form-control" }) ) class Meta : model = models.UserInfo fields = ["name" , "password" , "age" ]
重新定义的init方法,批量设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 class UserModelForm (forms.ModelForm): class Meta : model = models.UserInfo fields = ["name" , "password" , "age" ,] def __init__ (self, *args, **kwargs ): super ().__init__(*args, **kwargs) for name, field in self.fields.items(): field.widget.attrs = { "class" : "form-control" , "placeholder" : field.label
自定义类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BootStrapModelForm (forms.ModelForm): def __init__ (self, *args, **kwargs ): super ().__init__(*args, **kwargs) for name, field in self.fields.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 }
以后写的时候直接下面:
1 2 3 4 class UserEditModelForm (BootStrapModelForm ): class Meta : model = models.UserInfo fields = ["name" , "password" , "age" ,]
拆分功能(优化) 知道了上面的自定义类,我们就来优化一下: 首先先创建父类模板 再将之前定义的ModelForm类里的init方法删干净,继承BootStrapModelForm
这样算是完成了第一部的封装,我们看一下views.py的缩略图: 会发现还是有一些类,我们的类可以单独的去放在一个文件内,就比如是forms.py内,这样可以更加简介: 灰色的没用到的模块就删除掉 现在只剩下了函数,我们的函数也可以进一步进行拆分,就比如放在views文件夹内: 里面分别放置User,Pretty,depart这三个模块的py文件: views的py文件就删除了,接下来需要改的也就只是路由: 之后页面还是照常运行QWQ
管理员数据库 makemigrations+migrate
创建数据库
管理员列表 先自行插入数据库两条数据
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 {% extends 'layout.html' %} {% block content %} <div style ="margin-bottom: 10px;" class ="clearfix" > <a class ="btn btn-success" href ="/admin/add/" target ="_blank" > <span class ="glyphicon glyphicon-plus" aria-hidden ="true" > </span > 新建管理员 </a > <div style ="float: right;width: 300px;" > <form method ="get" > <div class ="input-group" > <input type ="text" class ="form-control" name ="q" placeholder ="Search for..." value ="{{ search }}" > <span class ="input-group-btn" > <button class ="btn btn-default" type ="submit" > <span class ="glyphicon glyphicon-search" aria-hidden ="true" > </span > </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 > ID</th > <th > 用户名</th > <th > 密码</th > <th > 操作</th > </tr > </thead > <tbody > {% for data in info %} <tr > <td > {{ data.id }}</td > <td > {{ data.username }}</td > <td > ******</td > <td > <a class ="btn btn-primary btn-xs" href ="#" > 编辑</a > <a class ="btn btn-danger btn-xs" href ="#" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > <nav aria-label ="Page navigation" > <ul class ="pagination" > {{ change }} </ul > </nav > </div > {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.shortcuts import redirect,renderfrom app01 import modelsfrom app01.utils.Jumpage import jumpfrom app01.utils.forms import AdminFormdef admin_list (request ): search={} value=request.GET.get('q' ,"" ) if value: search['username__contains' ]=value data=models.Admin.objects.filter (**search) Jumpage=jump(request,data) content={ "info" :Jumpage.queryset, "change" :Jumpage.HTMl(), "search" :value, } return render(request,'admin_list.html' ,content)
添加管理员 添加管理员需要以下几个步骤:
添加路由
添加html界面
添加class类
添加函数,函数内要引入,搜索框,分页框
我们还会发现,不管是我们的user,pretty还是admin的添加界面,用的都是同一个模板,所以为了避免文件过多,我们就添加一个公用模板add.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 {% extends 'layout.html' %} {% block content %} <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 > {% endblock %}
函数就长这样:跟我们之前说好的用法一样
1 2 3 4 5 6 7 8 9 10 def admin_add (request ): if request.method == 'GET' : form = AdminForm() return render(request, 'add.html' , {"form" : form,"title" :"新建管理员" }) form = AdminForm(data=request.POST) if form.is_valid(): form.save() return redirect('/admin/list/' ) else : return render(request, 'add.html' , {"form" : form,"title" :"新建管理员" })
效果如下:确认密码 : 新建密码应该还要有个确认密码才标准对吧,然后密码输入的时候不能被看见输入了什么东西,所以我们来创建! 只需要在类里添加个字段即可,然后设置不可见就用插件widgets 添加一下字段即可,最终效果: 接下来添加验证2次输入是否一致:
1 2 3 4 5 6 def clean_confirm_passwd (self ): pwd=self.cleaned_data['password' ] password=self.cleaned_data['confirm_passwd' ] if pwd==password: return pwd raise ValidationError('两次输入的密码不一致!' )
最后为了安全性,我们一般对输入的密码进行加盐md5加密!,现在让我们来实现 首页的clean_xxx的意思都知道,是个HOOK函数,对指定输入的参数检验的,利用这个函数来进行加密和判断
1 2 3 4 5 6 7 from django.conf import settings import hashlibdef md5 (data ): str =data+settings.SECRET_KEY md5=hashlib.md5(str .encode('utf-8' )).hexdigest() return md5
这就是我们的md5加密,hashlib.md5里必须放一个字节型的,所以要encode 里面的SECRET_KEY是django自动生成的: 接下来就是修改函数了
1 2 3 4 5 6 7 8 9 10 11 from app01.utils.md5 import md5def clean_confirm_passwd (self ): pwd=self.cleaned_data['password' ] print (self.cleaned_data) password=self.cleaned_data['confirm_passwd' ] if pwd!=md5(password): raise ValidationError('两次输入的密码不一致!' ) def clean_password (self ): pwd=self.cleaned_data['password' ] return md5(pwd)
在数据库看看: 成功加盐加密!告一段落
编辑和删除和重置密码管理员 这个就照葫芦画瓢就好了,我们之前在新建靓号的时候已经做过很多次了,要求大致如下
id不存在跳转错误页面
成功编辑用户名
不能存在同样的用户名
成功重置密码,重置的密码不可以和之前一样
删除就没啥好说的,一模一样
先来编辑一下管理员吧:
类
URL
函数
html1 2 3 4 {% extends 'layout.html' %} {% block content %} <div class ="alert alert-danger" role="alert" >{{ msg }}</div> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 class AdminEditForm (BootStrapModelForm ): class Meta : model =models .Admin fields =['username '] def clean_username (self ): name =self .cleaned_data ['username '] exists = models .Admin .objects .filter (username =name ).exclude (id =self .instance .pk ).exists () if exists : raise ValidationError ('用户名已存在') return name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def admin_edit (request,nid ): row_obj=models.Admin.objects.filter (id =nid).first() if not row_obj: return render(request,'error.html' ,{"msg" :"不存在的数据" }) if request.method=='GET' : form=AdminEditForm(instance=row_obj) content={ 'title' :'编辑用户名' , 'form' :form } return render(request,'add.html' ,content) form=AdminEditForm(data=request.POST,instance=row_obj) if form.is_valid(): form.save() return redirect('/admin/list/' ) return render(request,'add.html' ,{"form" :form,"title" :"编辑用户名" })
这波就设置完了修改用户名的界面和错误界面:
接下来设置一下删除界面,这就很简单了:
1 2 3 def admin_del (request,nid ): models.Admin.objects.filter (id =nid).delete() return redirect('/admin/list/' )
最后就是重置一下密码了,这也是比较有难度的
Cookie和Session 老生常谈
用户认证-基本实现方式 先添加路由,页面和函数,内容如下:
1 2 3 4 5 6 7 class LoginForm (forms.Form): username=forms.CharField(label='用户名' ,widget=forms.TextInput(attrs={"class" :"form-control" })) password=forms.CharField(label='密码' ,widget=forms.PasswordInput(attrs={"class" :"form-control" })) 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 {% 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/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 >
加入我们字段很多的话,我们也是可以直接把BootstrapModelForm类的init方法拿过来的,即使一个是form一个是modelform
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class LoginForm (forms.Form): username=forms.CharField(label='用户名' ,widget=forms.TextInput) password=forms.CharField(label='密码' ,widget=forms.PasswordInput) def __init__ (self, *args, **kwargs ): super ().__init__(*args, **kwargs) for name, field in self.fields.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} def login (request ): form=LoginForm() return render(request,'login.html' ,{"form" :form})
都是可以滴~ 然后关于BootStrapModelForm类,我们可以进一步简化,由于我们这次用的是forms,所以用不了ModelForm,那我们再创建一个Form用的就好了: 然后init重复了,我们再定义一个类来放init,再让我们BoostarpModelForm和BootstapForm都继承这个类就好: 们的页面仍然是一样的
设置错误信息:
为什么会有必填呢,因为我们的LoginForm类定义的字段里其实默认有个require
参数,他默认为True:
接下来就来验证输入的密码是否和输入的密码一样: 接下来就是添加session了 只要写上一句request.session['info']=xxx
,django就会处理: 首先返回客户端一个session,然后再服务端储存这个session的值为info=xxxx
1 2 request.session['info' ]= {"id" :row_obj.id ,"name" :row_obj.username} return redirect('/admin/list/' )
然后服务端的session数据存到数据库里了: expire_date表示到期时间 现在是如果有些没有登录,就需要设置权限了: 这时候要在adminlist页面判断session了,下一P就讲中间件来处理这个
用户认证-中间件处理权限 可以再django内部检查用户有没有登录
1 2 info=request.session['info' ] print (info)
如果我们已经登录了,会返回sessionid,然后后台会获得数据 如果没有: 这就是个雷区了,假如我们info=request.session['info']
,假如没有session是不会返回none的,所以要改成info=request.session.get('info')
,这样就不会报错了
我们发现还有一个问题,就是并没有进行鉴权,一般来说我们进行鉴权的流程如下:
1 2 3 info = request.session['info' ] if not info: return redirect('/login/' )
这样没登录的用户就会跳转到登录界面了,我们的设计理念就是假如没有登录,不能使用所有功能,一律跳转到登录界面,如果用上述方法鉴权,那要复制粘贴20多份,一个函数一份,就很麻烦,对于这个问题,django给出了答案,那就是中间件了,在django中,中间件就是一个类 我们先创建一个MiddleWare
文件夹来存放中间件类,创建auth.py文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from django.utils.deprecation import MiddlewareMixinclass M1 (MiddlewareMixin ): def process_request (self,request ): print ('M1 in' ) def process_response (self,request,response ): print ('M1 Out' ) return response class M2 (MiddlewareMixin ): def process_request (self, request ): print ('M2 in' ) def process_response (self, request,response ): print ('M2 Out' ) return response
和app,数据库一样,中间件也是要去注册的: 这样的话我们随便访问一个界面: 流程就是上面的图片所对应的,这样我们就对中间件有个比较好的认知了,我们删掉一个,目前一个就够了 接下来就写个鉴权的中间件:
1 2 3 4 5 6 7 8 9 10 11 12 from django.utils.deprecation import MiddlewareMixinfrom django.shortcuts import redirectclass Auth (MiddlewareMixin ): def process_request (self,request ): info=request.session.get('info' ) if info: return return redirect('/login/' ) def process_response (self,request,response ): return response
但是这里会有个小BUG: 在未登入的状态下访问一个页面: 会无限重定向,仔细想想也是,因为既然没登录,每次重定向都又要再次经过中间件,我们重定向的模式图: 是第二种方式,会一直重复去递归,为了解决这个问题,就得去写个白名单,也就是不需要登录就可以访问的界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from django.utils.deprecation import MiddlewareMixinfrom django.shortcuts import redirectclass Auth (MiddlewareMixin ): def process_request (self,request ): if request.path_info=='/login/' : return info=request.session.get('info' ) if info: return return redirect('/login/' ) def process_response (self,request,response ): return response
告一段落
账号注销 其实很简单,就是一个指令request.sesssion.clear()
就可以清除当前的session,和之前的删除用户是一个原理:
1 2 3 def logout (request ): request.session.clear() return redirect('/login/' )
还有一个小地方,就是我们登录后要显示用户名,这下我们一直没用着的request起作用了
图片验证码(显示) 1 2 3 4 5 6 7 8 9 10 11 12 <div class ="form-group" > <label for ="id_code" > 图片验证码</label > <div class ="row" > <div class ="col-xs-7" > {{ form.code }} <span style ="color: red;" > {{ form.code.errors.0 }}</span > </div > <div class ="col-xs-5" > <img id ="image_code" src ="{%static 'img/code.jpg' " %}" style ="width: 125px;" > </div > </div > </div >
先加一下验证码的HTML代码:再把验证码图片拿过来大致就有个效果: 但是这样肯定是不可以的,我们的验证码应该是动态的,这时候就得用找pillow模块去生成验证码了,pip insatll pillow
即可安装该模块,然后该模块的一些用法:https://www.cnblogs.com/xuyaping/p/7155088.html
1 2 3 4 5 6 7 from PIL import Imageimg = Image.new(mode='RGB' , size=(120 , 30 ), color=(255 , 255 , 255 )) with open ('code.png' , 'wb' ) as f: img.save(f, format ='png' )
就创建了一个图片 接下来一些功能自己跟着博客学一下就好了,接下来直接把生成随机验证码的函数拿过来:
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 from PIL import Image,ImageFont,ImageFilter,ImageDrawimport randomdef check_code (width=120 , height=30 , char_length=5 , font_file='Badaboom BB.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 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) if __name__ == '__main__' : img,code = check_code() print (img)
大致的意思挺好看懂的,之后我们来看看返回值给了些什么: 还挺好的是吧,接下来就是如何让他放在浏览器里了 接下来吧HTML中加载图片的地址改为一个URL,一个路由: 之后编写函数: 同样的,验证码也是我们不登录也能查看的,所以得改一下验证系统: 将之前的函数内容复制到:字体要留在根目录要记得,坑区 定义一个函数:
1 2 3 4 5 6 def image_code (request ): img,code_str=check_code() stream = BytesIO() img.save(stream, 'png' ) 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 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(): input_code = form.cleaned_data.pop('code' ) code = request.session.get('image_code' , "" ) if code.upper() != input_code.upper(): form.add_error('code' , '验证码错误' ) return render(request, 'login.html' , {"form" : form}) row_obj=models.Admin.objects.filter (**form.cleaned_data).first() if not row_obj: form.add_error('password' ,'用户名或密码错误' ) return render(request,'login.html' ,{"form" :form}) request.session['info' ]= {"id" :row_obj.id ,"name" :row_obj.username} request.session.set_expiry(60 *60 *24 *7 ) return redirect('/admin/list/' ) return render(request,'login.html' ,{"form" :form})
1 2 3 4 5 6 7 8 9 def image_code (request ): img,code_str=check_code() stream = BytesIO() img.save(stream, 'png' ) request.session['image_code' ]=code_str request.session.set_expiry(60 ) return HttpResponse(stream.getvalue())
ajax 暂时只能到这了,这里有点就涉及盲区了,没学过前端啊