Django大全1
django获取请求参数的方式(重点)
django是如何传递参数的(全面)
1.通过get请求发送的请求可以通过request.GET.get('xxx')来获取; 如:在url中?号后面传的参数都可以通过request.GET中来获取 2.通过post请求发送的请求可以通过request.POST.get('xxx')来获取(Json格式的无法获取); 如:通过post发送的表单数据 3.通过post发送的json格式的数据可以在request.body中获取 4.在请求头中携带的参数可以通过request.META.get('xxx')来获取, # 注意:获取自定义的请求头属性值时,需要添加前缀 HTTP_ 并转成大写,作为键来获取值 # 注意:获取http请求头内容,使用request.META.get("header的key")来获取; # header的key必须大写,如果连接符是横线"-"要改成下划线"_" 5.文件数据通过request.FILES.get来获取
一、python的三大主流框架
1 """ 2 django 3 特点:大而全 自带的功能特别特别特别的多 类似于航空母舰 4 不足之处: 5 有时候过于笨重 6 7 flask 8 特点:小而精 自带的功能特别特别特别的少 类似于游骑兵 9 第三方的模块特别特别特别的多,如果将flask第三方的模块加起来完全可以盖过django 10 并且也越来越像django 11 不足之处: 12 比较依赖于第三方的开发者 13 14 tornado 15 特点:异步非阻塞 支持高并发 16 牛逼到甚至可以开发游戏服务器 17 不足之处: 18 暂时你不会 19 """ 20 A:socket部分 21 B:路由与视图函数对应关系(路由匹配) 22 C:模版语法 23 24 django 25 A用的是别人的 wsgiref模块 26 B用的是自己的 27 C用的是自己的(没有jinja2好用 但是也很方便) 28 29 flask 30 A用的是别人的 werkzeug(内部还是wsgiref模块) 31 B自己写的 32 C用的别人的(jinja2) 33 34 tornado 35 A,B,C都是自己写的
注意事项
1 # 如何让你的计算机能够正常的启动django项目 2 1.计算机的名称不能有中文 3 2.一个pycharm窗口只开一个项目 4 3.项目里面所有的文件也尽量不要出现中文 5 4.python解释器尽量使用3.4~3.6之间的版本 6 (如果你的项目报错 你点击最后一个报错信息 7 去源码中把逗号删掉) 8 9 # django版本问题 10 1.X 2.X 3.X(直接忽略) 11 1.X和2.X本身差距也不大 我们讲解主要以1.X为例 会讲解2.X区别 12 公司之前用的1.8 满满过渡到了1.11版本 有一些项目用的2.0 13 14 # django安装 15 pip3 install django==1.11.11 16 如果已经安装了其他版本 无需自己卸载 17 直接重新装 会自动卸载安装新的 18 19 如果报错 看看是不是timeout 如果是 那么只是网速波动 20 重新安装即可 21 22 验证是否安装成功的方式1 23 终端输入django-admin看看有没有反应 24
两种软件开发架构
cs架构 bs架构 # 本质bs也是cs架构
动静态网页
1 """ 2 静态网页 3 页面上的数据是直接写死的 万年不变 4 动态网页 5 数据是实时获取的 6 eg: 7 1.后端获取当前时间展示到html页面上 8 2.数据是从数据库中获取的展示到html页面上 9 """ 10 11 # 动态网页制作 12 13 # 1.先建一个templates/03 mytime.html的html文件 14 import datetime 15 def get_time(env): 16 current_time = datetime.datetime.now().strftime('%Y-%m-%d %X') 17 # 如何将后端获取到的数据"传递"给html文件? 18 with open(r'templates/03 mytime.html','r',encoding='utf-8') as f: 19 data = f.read() 20 # data就是一堆字符串 21 data = data.replace('dwadasdsadsadasdas',current_time) # 在后端将html页面处理好之后再返回给前端 22 return data 23 24 25 # 将一个字典传递给html文件 并且可以在文件上方便快捷的操作字典数据 26 27 # 1.先建一个templates/04 get_dict.html的html文件,然后如下: 28 from jinja2 import Template 29 def get_dict(env): 30 user_dic = {'username':'jason','age':18,'hobby':'read'} 31 with open(r'templates/04 get_dict.html','r',encoding='utf-8') as f: 32 data = f.read() 33 tmp = Template(data) 34 res = tmp.render(user=user_dic) 35 # 给get_dict.html传递了一个值 页面上通过变量名user就能够拿到user_dict 36 return res 37 38 # 后端获取数据库中数据展示到前端页面
1 pip3 install jinja2 2 """模版语法是在后端起作用的""" 3 4 # 模版语法(非常贴近python语法) 5 {{ user }} # 先两个大括号,里面可以用对象中的.来取值 也可以用字典的k来取值 6 {{ user.get('username')}} 7 {{ user.age }} 8 {{ user['hobby'] }} 9 10 11 {% for user_dict in user_list %} 12 <tr> 13 <td>{{ user_dict.id}}</td> 14 <td>{{ user_dict.username}}</td> 15 <td>{{ user_dict.password}}</td> 16 <td>{{ user_dict.hobby}}</td> 17 </tr> 18 {% endfor%}
""" wsgiref模块 1.请求来的时候解析http格式的数据 封装成大字典 2.响应走的时候给数据打包成符合http格式 再返回给浏览器 """
创建Django项目的两种方式:
1.命令行创建
2.pycharm创建
1 # 方式一:命令行操作 2 # 1.创建Django项目 3 """ 4 你可以先切换到对应的D盘 然后再创建 5 """ 6 1.创建项目语法:django-admin startproject 项目名 7 django-admin startproject mysite 8 9 mysite文件夹 10 manage.py 11 mysite文件夹 12 __init__.py 13 settings.py 14 urls.py 15 wsgi.py 16 # 2.启动django项目 17 """ 18 一定要先切换到项目目录下 19 cd /mysite 20 """ 21 2.启动Django语法:python3 manage.py runserver 22 python3 manage.py runserver # 后面可以跟ip:port 23 # 没写默认是本机的ip端口号http://127.0.0.1:8000/ 24 25 # 3.创建应用 26 """ 27 Next, start your first app by running python manage.py startapp [app_label]. 28 """ 29 3.创建应用语法:python manage.py startapp app名称 30 python manage.py startapp app01 31 应用名应该做到见名知意 32 user 33 order 34 web 35 ... 36 但是我们教学统一就用app01/02/03/04 37 38 有很多文件 39 40 # 方式二:pycharm操作 41 # 1 new project 选择左侧第二个django即可 42 43 # 2 启动 44 1.还是用命令行启动 45 2.点击绿色小箭头即可 46 47 # 3 创建应用 48 1.pycharm提供的终端直接输入完整命令 49 2.pycharm 50 tools 51 run manage.py task提示(前期不要用 给我背完整命令) 52 # 4 修改端口号以及创建server 53 edit confi.... 54 55
创建Django的app应用
注册应用两种方式:1.全写,2.简写
1 """ 2 django是一款专门用来开发app的web框架 3 4 django框架就类似于是一所大学(空壳子) 5 app就类似于大学里面各个学院(具体功能的app) 6 比如开发淘宝 7 订单相关 8 用户相关 9 投诉相关 10 创建不同的app对应不同的功能 11 12 选课系统 13 学生功能 14 老师功能 15 16 一个app就是一个独立的功能模块 17 """ 18 ***********************创建的应用一定要去配置文件中注册********************** 19 INSTALLED_APPS = [ 20 'django.contrib.admin', 21 'django.contrib.auth', 22 'django.contrib.contenttypes', 23 'django.contrib.sessions', 24 'django.contrib.messages', 25 'django.contrib.staticfiles', 26 'app01.apps.App01Config', # 全写 27 'app01', # 简写 28 ] 29 # 创建出来的的应用第一步先去配置文件中注册 其他的先不要给我干 30 ps:你在用pycharm创建项目的时候 pycharm可以帮你创建一个app并且自动注册 31 ***********************************************************************
django主要文件介绍
1 -mysite项目文件夹 2 --mysite文件夹 3 ---settings.py 配置文件 4 ---urls.py 路由与视图函数对应关系(路由层) 5 ---wsgi.py wsgiref模块(不考虑) 6 --manage.py django的入口文件 7 --db.sqlite3 django自带的sqlite3数据库(小型数据库 功能不是很多还有bug) 8 --app01文件夹 9 ---admin.py django后台管理 10 ---apps.py 注册使用 11 ---migrations文件夹 数据库迁移记录 12 ---models.py 数据库相关的 模型类(orm) 13 ---tests.py 测试文件 14 ---views.py 视图函数(视图层) 15
命令行与pycharm创建Django项目的区别
1 # 1 命令行创建不会自动有templates文件夹 需要你自己手动创建 而pycharm会自动帮你创建templates并且还会自动在配置文件中配置对应的路径 2 # pycharm创建 3 TEMPLATES = [ 4 { 5 'BACKEND': 'django.template.backends.django.DjangoTemplates', 6 'DIRS': [os.path.join(BASE_DIR, 'templates')] 7 ] 8 # 命令行创建 9 TEMPLATES = [ 10 { 11 'BACKEND': 'django.template.backends.django.DjangoTemplates', 12 'DIRS': [], 13 ] 14 """ 15 也就意味着你在用命令创建django项目的时候不单单需要创建templates文件夹还需要去配置文件中配置路径 16 'DIRS': [os.path.join(BASE_DIR, 'templates')] 17 """ 18
Django小白必会三板斧(HttpResponse/render/redirect)
1 """ 2 HttpResponse 3 返回字符串类型的数据 4 5 render 6 返回html文件的 7 8 redirect 9 重定向 10 return redirect('https://www.mzitu.com/') 11 return redirect('/home/') 12 """ 13 14 # views.py文件 15 from django.shortcuts import HttpResponse,render,redirect 16 17 return HttpResponse('字符串') 18 19 return render(request,'login.html') 20 def ab_render(request): 21 # 视图函数必须要接受一个形参request 22 user_dict = {'username':'jason','age':18} 23 # 第一种传值方式:更加的精确 节省资源 24 # return render(request,'01 ab_render.html',{'data':user_dict,'date':123}) 25 # 第二种传值方式:当你要传的数据特别多的时候 26 """locals会将所在的名称空间中所有的名字全部传递给html页面""" 27 return render(request,'01 ab_render.html',locals()) 28 29 return redirect(url)
static静态文件配置
1 # 登陆功能 2 3 """ 4 我们将html文件默认都放在templates文件夹下 5 我们将网站所使用的静态文件默认都放在static文件夹下 6 7 静态文件 8 前端已经写好了的 能够直接调用使用的文件 9 网站写好的js文件 10 网站写好的css文件 11 网站用到的图片文件 12 第三方前端框架 13 ... 14 拿来就可以直接使用的 15 """ 16 # django默认是不会自动帮你创建static文件夹 需要你自己手动创建 17 一般情况下我们在static文件夹内还会做进一步的划分处理 18 -static 19 --js 20 --css 21 --img 22 其他第三方文件 23 24 """ 25 在浏览器中输入url能够看到对应的资源 26 是因为后端提前开设了该资源的接口 27 如果访问不到资源 说明后端没有开设该资源的接口 28 29 http://127.0.0.1:8000/static/bootstrap-3.3.7-dist/css/bootstrap.min.css 30 """ 31 32 # 静态文件配置 33 34 """ 35 **************************************************************** 36 当你在写django项目的时候 可能会出现后端代码修改了但是前端页面没有变化的情况 37 1.你在同一个端口开了好几个django项目 38 一直在跑的其实是第一个django项目 39 40 2.浏览器缓存的问题 41 settings 42 network 43 disable cache 勾选上 44 ***************************************************************** 45 """ 46 47 STATIC_URL = '/static/' # 类似于访问静态文件的令牌 48 """如果你想要访问静态文件 你就必须以static开头""" 49 """ 50 /static/bootstrap-3.3.7-dist/js/bootstrap.min.js 51 52 /static/令牌 53 取列表里面从上往下依次查找 54 bootstrap-3.3.7-dist/js/bootstrap.min.js 55 都没有才会报错 56 """ 57 # 静态文件配置 58 STATICFILES_DIRS = [ 59 os.path.join(BASE_DIR,'static'), 60 os.path.join(BASE_DIR,'static1'), 61 os.path.join(BASE_DIR,'static2'), 62 ] 63 64 # 静态文件动态解析 65 {% load static %} 66 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> 67 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> 68 69 70 # form表单默认是get请求数据 71 http://127.0.0.1:8000/login/?username=jason&password=123 72 """ 73 form表单action参数 74 1.不写 默认朝当前所在的url提交数据 75 2.全写 指名道姓 76 3.只写后缀 /login/ 77 """ 78 79 80 # 在前期我们使用django提交post请求的时候 需要取配置文件中注释掉一行代码 81 MIDDLEWARE = [ 82 'django.middleware.security.SecurityMiddleware', 83 'django.contrib.sessions.middleware.SessionMiddleware', 84 'django.middleware.common.CommonMiddleware', 85 # 'django.middleware.csrf.CsrfViewMiddleware', 86 'django.contrib.auth.middleware.AuthenticationMiddleware', 87 'django.contrib.messages.middleware.MessageMiddleware', 88 'django.middleware.clickjacking.XFrameOptionsMiddleware', 89 ]
django开放对应接口目录(media)
当路由匹配到media,后面的path以及document_root参数都会传给serve函数,直接可以将media文件夹内部的文件暴露给外界,外界可以通过这个路径访问media文件下的文件。
from django.contrib import admin from django.urls import path,re_path,include from django.views.static import serve from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), re_path('media/(?P<path>.*)',serve,{"document_root":settings.MEDIA_ROOT}) ]
request对象方法初识
1 request.method # 返回请求方式 并且是全大写的字符串形式 <class 'str'> 2 request.POST # 获取用户post请求提交的普通K:V数据不包含文件,可以看成字典格式 3 request.POST.get() # 只获取列表最后一个元素 拿到的是字符串 4 request.POST.getlist() # 直接将列表取出 拿到的就是列表 5 request.GET # 获取用户提交的get请求数据 6 request.GET.get() # 只获取列表最后一个元素 7 request.GET.getlist() # 直接将列表取出 8 """ 9 get请求携带的数据是有大小限制的 大概好像只有4KB左右 10 而post请求则没有限制 11 """ 12 13 def login(request): 14 # 返回一个登陆界面 15 """ 16 get请求和post请求应该有不同的处理机制 17 :param request: 请求相关的数据对象 里面有很多简易的方法 18 :return: 19 """ 20 # print(type(request.method)) # 返回请求方式 并且是全大写的字符串形式 <class 'str'> 21 # if request.method == 'GET': 22 # print('来了 老弟') 23 # return render(request,'login.html') 24 # elif request.method == 'POST': 25 # return HttpResponse("收到了 宝贝") 26 27 if request.method == 'POST': 28 return HttpResponse("收到了 宝贝") 29 return render(request, 'login.html')
django项目pycharm链接数据库(MySQL)
1 """ 2 三个位置查找数据库相关 3 右侧上方database 4 左下方database 5 配置里面的plugins插件搜索安装 6 7 再没有卸载pycharm重新装 8 9 pycharm可以充当很多款数据库软件的客户端 10 11 参考截图和视频操作即可 12 需要提前创建好库 13 步骤如下: 14 第1步:点击左下角download missing driver files 下载驱动 15 第2步:点击中间Driver:后面的MySQL 如果不行点MySQL for 5.1 然后重复步骤1下载驱动 16 第3步:填写user和password、数据库 然后点击test connection 如果没连接成功 换5.1版本试下 17 第4步:连接成功直接点击apply 然后点击ok即可 18 19 """
django链接数据库(MySQL)
1 # 默认用的是sqkite3 2 DATABASES = { 3 'default': { 4 'ENGINE': 'django.db.backends.sqlite3', 5 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 6 } 7 } 8 9 # django链接MySQL 10 1.第一步配置文件中配置 11 DATABASES = { 12 'default': { 13 'ENGINE': 'django.db.backends.mysql', 14 'NAME': 'day60', # 对应数据库名 15 'USER':'root', 16 'PASSWORD':'admin123', 17 'HOST':'127.0.0.1', 18 'PORT':3306, 19 'CHARSET':'utf8' 20 } 21 } 22 2.代码声明 23 django默认用的是mysqldb模块链接MySQL 24 但是该模块的兼容性不好 需要手动改为用pymysql链接 25 26 你需要告诉django不要用默认的mysqldb还是用pymysql 27 # 在项目名下的init或者任意的应用名下的init文件中书写以下代码都可以 28 import pymysql 29 pymysql.install_as_MySQLdb() 30
二、Django ORM
1 """ 2 ORM. 对象关系映射 3 作用:能够让一个不用sql语句的小白也能够通过python 面向对象的代码简单快捷的操作数据库 4 不足之处:封装程度太高 有时候sql语句的效率偏低 需要你自己写SQL语句 5 6 类 表 7 8 对象 记录/字段 9 10 对象属性 记录某个字段对应的值 11 12 13 应用下面的models.py文件 14 """ 15 16 # 1 先去models.py中书写一个类 17 class User(models.Model): 18 # id int primary_key auto_increment 19 id = models.AutoField(primary_key=True) 20 # username varchar(32) 21 username = models.CharField(max_length=32) 22 # password int 23 password = models.IntegerField() 24 25 *************************# 2 数据库迁移命令************************* 26 python3 manage.py makemigrations 将操作记录记录到小本本上(migrations文件夹) 27 28 python3 manage.py migrate 将操作真正的同步到数据库中 29 # 只要你修改了models.py中跟数据库相关的代码 就必须重新执行上述的两条命令 30 ****************************************************************** 31 32 class User(models.Model): 33 # id int primary_key auto_increment 34 id = models.AutoField(primary_key=True,verbose_name='主键') 35 # username varchar(32) 36 username = models.CharField(max_length=32,verbose_name='用户名') 37 """ 38 CharField必须要指定max_length参数 不指定会直接报错 39 verbose_name该参数是所有字段都有的 就是用来对字段的解释 40 """ 41 # password int 42 password = models.IntegerField(verbose_name='密码') 43 44 45 class Author(models.Model): 46 # 由于一张表中必须要有一个主键字段 并且一般情况下都叫id字段 47 # 所以orm当你不定义主键字段的时候 orm会自动帮你创建一个名为id主键字段 48 # 也就意味着 后续我们在创建模型表的时候如果主键字段名没有额外的叫法 那么主键字段可以省略不写 49 # username varchar(32) 50 username = models.CharField(max_length=32) 51 # password int 52 password = models.IntegerField() 53
字段的增删改查
1 # 字段的增加 三种方式如下: 2 1.可以在终端内直接给出默认值 3 2.该字段可以为空 null=True 4 info = models.CharField(max_length=32,verbose_name='个人简介',null=True) 5 3.直接给字段设置默认值 default=xxx 6 hobby = models.CharField(max_length=32,verbose_name='兴趣爱好',default='study') 7 8 # 字段的修改 9 直接修改代码然后执行数据库迁移的两条命令即可! 10 11 # 字段的删 12 直接注释对应的字段然后执行数据库迁移的两条命令即可! 13 执行完毕之后字段对应的数据也都没有了 14 15 """ 16 在操作models.py的时候一定要细心 17 千万不要注释一些字段 18 执行迁移命令之前最好先检查一下自己写的代码 19 """ 20 21 # 个人建议:当你离开你的计算机之后一定要锁屏
数据的增删改查
1 # 查 2 res = models.User.objects.filter(username=username) 3 """ 4 返回值你先看成是列表套数据对象的格式 5 它也支持索引取值 切片操作 但是不支持负数索引 6 它也不推荐你使用索引的方式取值 7 user_obj = models.User.objects.filter(username=username).first() 8 """ 9 filter括号内可以携带多个参数 参数与参数之间默认是and关系 10 你可以把filter联想成where记忆 11 12 13 14 # 增 15 # 第一种增加 16 from app01 import models 17 res = models.User.objects.create(username=username,password=password) 18 # 返回值就是当前被创建的对象本身 19 20 # 第二种增加 21 user_obj = models.User(username=username,password=password) 22 user_obj.save() # 保存数据 23 24 25 # 先将数据库中的数据全部展示到前端 然后给每一个数据两个按钮 一个编辑一个删除 26 27 # 查看 28 def userlist(request): 29 # 查询出用户表里面所有的数据 30 # 方式1 31 # data = models.User.objects.filter() 32 # print(data) 33 # 方式2 34 user_queryset = models.User.objects.all() 35 # return render(request,'userlist.html',{'user_queryset':user_queryset}) 36 return render(request,'userlist.html',locals()) 37 38 # 编辑功能 39 # 点击编辑按钮朝后端发送编辑数据的请求 40 """ 41 如何告诉后端用户想要编辑哪条数据? 42 将编辑按钮所在的那一行数据的主键值发送给后端 43 利用url问号后面携带参数的方式 44 45 {% for user_obj in user_queryset %} 46 <tr> 47 <td>{{ user_obj.id }}</td> 48 <td>{{ user_obj.username }}</td> 49 <td>{{ user_obj.password }}</td> 50 <td> 51 <a href="/edit_user/?user_id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a> 52 <a href="" class="btn btn-danger btn-xs">删除</a> 53 </td> 54 </tr> 55 {% endfor %} 56 """ 57 58 # 后端查询出用户想要编辑的数据对象 展示到前端页面供用户查看和编辑 59 def edit_user(request): 60 # 获取url问号后面的参数 61 edit_id = request.GET.get('user_id') 62 # 查询当前用户想要编辑的数据对象 63 edit_obj = models.User.objects.filter(id=edit_id).first() 64 65 if request.method == "POST": 66 username = request.POST.get('username') 67 password = request.POST.get('password') 68 # 去数据库中修改对应的数据内容 69 # 修改数据方式1 70 # models.User.objects.filter(id=edit_id).update(username=username,password=password) 71 """ 72 将filter查询出来的列表中所有的对象全部更新 批量更新操作 73 只修改被修改的字段 74 """ 75 76 # 修改数据方式2 77 edit_obj = models.User.objects.filter(id=edit_id).first() 78 edit_obj.username = username 79 edit_obj.password= password 80 edit_obj.save() 81 """ 82 上述方式2的方法,当字段特别多的时候效率会非常的低 83 从头到尾将数据的所有字段全部更新一边 无论该字段是否被修改 84 """ 85 86 # 跳转到数据的展示页面 87 return redirect('/userlist/') 88 89 90 91 # 将数据对象展示到页面上 92 return render(request,'edit_user.html',locals()) 93 94 # 删除功能 95 """ 96 跟编辑功能逻辑类似 97 def delete_user(request): 98 # 获取用户想要删除的数据id值 99 delete_id = request.GET.get('user_id') 100 # 直接去数据库中找到对应的数据删除即可 101 models.User.objects.filter(id=delete_id).delete() 102 """ 103 批量删除 104 """ 105 # 跳转到展示页面 106 107 return redirect('/userlist/') 108 109 """ 110 # 真正的删除功能应该需要二次确认 我们这里先不做后面会讲 111 # 删除数据内部其实并不是真正的删除 我们会给数据添加一个标识字段用来表示当前数据是否被删除了,如果数据被删了仅仅只是讲字段修改一个状态 112 username password is_delete 113 jason 123 0 114 egon 123 1 115
django的orm中如何创建表关系
1 """ 2 表与表之间的关系 3 一对多 Foreignkey 4 5 多对多 ManyToMany 6 7 一对一 OneToOne 8 9 没有关系 10 11 判断表关系的方法:换位思考 12 """ 13 图书表 14 15 出版社表 16 17 作者表 18 19 作者详情表 20 21 22 """ 23 图书和出版社是一对多的关系 外键字段建在多的那一方 book 24 25 图书和作者是多对多的关系 需要创建第三张表来专门存储 26 27 作者与作者详情表是一对一 28 """ 29 from django.db import models 30 31 # Create your models here. 32 33 34 # 创建表关系 先将基表创建出来 然后再添加外键字段 35 class Book(models.Model): 36 title = models.CharField(max_length=32) 37 price = models.DecimalField(max_digits=8,decimal_places=2) 38 # 总共八位 小数点后面占两位 39 """ 40 图书和出版社是一对多 并且书是多的一方 所以外键字段放在书表里面 41 """ 42 publish = models.ForeignKey(to='Publish') # 默认就是与出版社表的主键字段做外键关联 43 """ 44 如果字段对应的是ForeignKey 那么会orm会自动在字段的后面加_id 45 如果你自作聪明的加了_id那么orm还是会在后面继续加_id 46 47 后面在定义ForeignKey的时候就不要自己加_id 48 """ 49 50 51 """ 52 图书和作者是多对多的关系 外键字段建在任意一方均可 但是推荐你建在查询频率较高的一方 53 """ 54 authors = models.ManyToManyField(to='Author') 55 """ 56 authors是一个虚拟字段 主要是用来告诉orm 书籍表和作者表是多对多关系 57 让orm自动帮你创建第三张关系表 58 """ 59 60 61 class Publish(models.Model): 62 name = models.CharField(max_length=32) 63 addr = models.CharField(max_length=32) 64 65 66 class Author(models.Model): 67 name = models.CharField(max_length=32) 68 age = models.IntegerField() 69 """ 70 作者与作者详情是一对一的关系 外键字段建在任意一方都可以 但是推荐你建在查询频率较高的表中 71 """ 72 author_detail = models.OneToOneField(to='AuthorDetail') 73 """ 74 OneToOneField也会自动给字段加_id后缀 75 所以你也不要自作聪明的自己加_id 76 """ 77 78 class AuthorDetail(models.Model): 79 phone = models.BigIntegerField() # 或者直接字符类型 80 addr = models.CharField(max_length=32) 81 82 83 """ 84 orm中如何定义三种关系 85 publish = models.ForeignKey(to='Publish') # 默认就是与出版社表的主键字段做外键关联 86 87 authors = models.ManyToManyField(to='Author') 88 89 author_detail = models.OneToOneField(to='AuthorDetail') 90 91 92 ForeignKey 93 OneToOneField 94 会自动在字段后面加_id后缀 95 """ 96 97 # 在django1.X版本中外键默认都是级联更新删除的 98 # 多对多的表关系可以有好几种创建方式 这里暂且先介绍一种 99 # 针对外键字段里面的其他参数 暂时不要考虑 如果感兴趣自己可以百度试试看
django请求生命周期流程图(必会)
1 # 每个人都要会画 这个图是你们后期复习django最好的一个梳理方式 2 3 4 # 扩展知识点 5 """ 6 缓存数据库 7 提前已经将你想要的数据准备好了 你来直接拿就可以 8 提高效率和响应时间 9 10 当你在修改你的数据的时候 你会发现数据并不是立刻修改完成的 11 而是需要经过一段时间才会修改 因为你继续访问时访问的是内存里面的缓存内容 并不是数据库中的内容 12 博客园 13 14 了解即可 15 """
三、路由层
路由匹配
1 # 路由匹配 2 url(r'test',views.test), 3 url(r'testadd',views.testadd) 4 """ 5 url方法第一个参数是正则表达式 6 只要第一个参数正则表达式能够匹配到内容 那么就会立刻停止往下匹配 7 直接执行对应的视图函数 8 9 你在输入url的时候会默认加斜杠 10 django内部帮你做到重定向 11 一次匹配不行 12 url后面加斜杠再来一次 13 """ 14 # 取消自动加斜杠 15 配置文件中: 16 APPEND_SLASH = False/True # 默认是自动加斜杠的 17 18 19 urlpatterns = [ 20 url(r'^admin/', admin.site.urls), 21 # 首页 22 url(r'^$',views.home), 23 # 路由匹配 24 url(r'^test/$',views.test), 25 url(r'^testadd/$',views.testadd), 26 # 尾页(了解) 27 url(r'',views.error), 28 ]
无名分组
1 """ 2 分组:就是给某一段正则表达式用小括号扩起来 3 """ 4 # urls.py 5 url(r'^test/(\d+)/',views.test) 6 7 # views.py 8 def test(request,xx): 9 print(xx) 10 return HttpResponse('test') 11 12 # 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数
有名分组
1 """ 2 可以给正则表达式起一个别名 3 """ 4 # urls.py 5 url(r'^testadd/(?P<year>\d+)',views.testadd) 6 7 8 # views.py 9 def testadd(request,year): 10 print(year) 11 return HttpResponse('testadd') 12 13 # 有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给后面的视图函数
无名有名是否可以混合使用
1 """ 2 嘻嘻 不能混用 3 但是同一个分组可以使用N多次 4 """ 5 6 # 单个的分组可以使用多次 7 url(r'^index/(\d+)/(\d+)/(\d+)/',views.index), 8 url(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),
反向解析
1 # 通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数 2 3 # 先给路由与视图函数起一个别名 4 url(r'^func_kkk/',views.func,name='ooo') 5 # 反向解析 两种方法: 6 # 1.后端反向解析 7 from django.shortcuts import render,HttpResponse,redirect,reverse 8 reverse('ooo') 9 # 2.前端反向解析 10 <a href="{% url 'ooo' %}">111</a> 11
无名有名分组反向解析
1 # 无名分组反向解析 2 url(r'^index/(\d+)/',views.index,name='xxx') 3 4 # 前端 5 {% url 'xxx' 123 %} 6 # 后端 7 reverse('xxx', args=(1,)) 8 9 """ 10 这个数字写代码的时候应该放什么 11 数字一般情况下放的是数据的主键值 数据的编辑和删除 12 url(r'^edit/(\d+)/',views.edit,name='xxx') 13 14 def edit(request,edit_id): 15 reverse('xxx',args=(edit_id,)) 16 17 {%for user_obj in user_queryset%} 18 <a href="{% url 'xxx' user_obj.id %}">编辑</a> 19 {%endfor%} 20 21 今天每个人都必须完成的作业(*******) 22 利用无名有名 反向解析 完成数据的增删改查 23 """ 24 25 26 27 # 有名分组反向解析 28 url(r'^func/(?P<year>\d+)/',views.func,name='ooo') 29 # 前端 30 <a href="{% url 'ooo' year=123 %}">111</a> 了解 31 <a href="{% url 'ooo' 123 %}">222</a> 记忆 32 33 # 后端 34 # 有名分组反向解析 写法1 了解 35 print(reverse('ooo',kwargs={'year':123})) 36 # 写法2 简便的写法 减少你的脑容量消耗 记跟无名一样的操作即可 37 print(reverse('ooo',args=(111,)))
路由分发
1 """ 2 django的每一个应用都可以有自己的templates文件夹 urls.py static文件夹 3 正是基于上述的特点 django能够非常好的做到分组开发(每个人只写自己的app) 4 作为组长 只需要将手下书写的app全部拷贝到一个新的django项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来 5 6 当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护 7 这个时候也可以利用路由分发来减轻总路由的压力 8 9 利用路由分发之后 总路由不再干路由与视图函数的直接对应关系 10 而是做一个分发处理 11 识别当前url是属于哪个应用下的 直接分发给对应的应用去处理 12 13 """ 14 # 1.路由分发 15 # 总路由 16 from django.conf.urls import url,include 17 from django.contrib import admin 18 19 from app01 import urls as app01_urls 20 from app02 import urls as app02_urls 21 urlpatterns = [ 22 url(r'^admin/', admin.site.urls), 23 24 url(r'^app01/',include(app01_urls)), # 只要url前缀是app01开头 全部交给app01处理 25 url(r'^app02/',include(app02_urls)) # 只要url前缀是app02开头 全部交给app02处理 26 ] 27 28 # 子路由 照常写 29 # app01 urls.py 30 from django.conf.urls import url 31 from app01 import views 32 urlpatterns = [ 33 url(r'^reg/',views.reg) 34 ] 35 36 # app02 urls.py 37 from django.conf.urls import url 38 from app02 import views 39 urlpatterns = [ 40 url(r'^reg/',views.reg) 41 ] 42 43 # 2.路由分发 终极写法 推荐使用 44 # 总路由 45 from django.conf.urls import url,include 46 from django.contrib import admin 47 48 urlpatterns = [ 49 url(r'^admin/', admin.site.urls), 50 url(r'^app01/',include('app01.urls')), 51 url(r'^app02/',include('app02.urls')) 52 ] 53 54 # 子路由 55 # app01 urls.py 56 from django.conf.urls import url 57 from app01 import views 58 urlpatterns = [ 59 url(r'^reg/',views.reg) 60 ] 61 # app02 urls.py 62 from django.conf.urls import url 63 from app02 import views 64 urlpatterns = [ 65 url(r'^reg/',views.reg) 66 ] 67 # 注意事项:总路由里面的url千万不能加$结尾
名称空间(了解)
1 # 当多个应用出现了相同的别名 我们研究反向解析会不会自动识别应用前缀 2 """ 3 正常情况下的反向解析是没有办法自动识别前缀的 4 """ 5 6 # 名称空间 7 # 总路由 8 url(r'^app01/',include('app01.urls',namespace='app01')), 9 url(r'^app02/',include('app02.urls',namespace='app02')) 10 # 解析的时候 11 # app01 12 urlpatterns = [ 13 url(r'^reg/',views.reg,name='reg') 14 ] 15 # app02 16 urlpatterns = [ 17 url(r'^reg/',views.reg,name='reg') 18 ] 19 20 reverse('app01:reg') 21 reverse('app02:reg') 22 23 {% url 'app01:reg' %} 24 {% url 'app02:reg' %} 25 # 其实只要保证名字不冲突 就没有必要使用名称空间 26 """ 27 一般情况下 有多个app的时候我们在子路由给url起别名的时候会加上app的前缀 28 这样的话就能够确保多个app之间名字不冲突的问题 29 """ 30 # 子路由 在别名前面添加一个前缀,建议以应用名作为前缀 31 urlpatterns = [ 32 url(r'^reg/',views.reg,name='app01_reg') 33 ] 34 urlpatterns = [ 35 url(r'^reg/',views.reg,name='app02_reg') 36 ]
伪静态(了解)
1 """ 2 静态网页 3 数据是写死的 万年不变 4 5 伪静态 6 将一个动态网页伪装成静态网页 7 8 为什么要伪装呢? 9 https://www.cnblogs.com/Dominic-Ji/p/9234099.html 10 伪装的目的在于增大本网站的seo查询力度 11 并且增加搜索引擎收藏本网上的概率 12 13 搜索引擎本质上就是一个巨大的爬虫程序 14 15 总结: 16 无论你怎么优化 怎么处理 17 始终还是干不过RMB玩家 18 """ 19 urlpatterns = [ 20 url(r'^reg.html',views.reg,name='app02_reg') 21 ]
虚拟环境(了解)
1 """ 2 在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境 3 该环境内只有该项目用到的模块 用不到一概不装 4 5 linux:缺什么才装什么 6 7 虚拟环境 8 你每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器 9 但是虚拟环境不要创建太多,是需要消耗硬盘空间的 10 11 扩展: 12 每一个项目都需要用到很多模块 并且每个模块版本可能还不一样 13 那我该如何安装呢? 一个个看一个个装??? 14 15 开发当中我们会给每一个项目配备一个requirements.txt文件 16 里面书写了该项目所有的模块即版本 17 你只需要直接输入一条命令即可一键安装所有模块即版本 18 """
django版本区别
1 """ 2 1.django1.X路由层使用的是url方法 3 而在django2.Xhe3.X版本中路由层使用的是path方法 4 url()第一个参数支持正则 5 path()第一个参数是不支持正则的 写什么就匹配什么 6 7 8 如果你习惯使用path那么也给你提供了另外一个方法 9 from django.urls import path, re_path 10 from django.conf.urls import url 11 12 re_path(r'^index/',index), 13 url(r'^login/',login) 14 2.X和3.X里面的re_path就等价于1.X里面的url 15 16 17 2.虽然path不支持正则 但是它的内部支持五种转换器 18 path('index/<int:id>/',index) 19 # 将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数 20 21 def index(request,id): 22 print(id,type(id)) 23 return HttpResponse('index') 24 25 26 27 str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式 28 int,匹配正整数,包含0。 29 slug,匹配字母、数字以及横杠、下划线组成的字符串。 30 uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。 31 path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?) 32 33 3.除了有默认的五个转换器之外 还支持自定义转换器(了解) 34 class MonthConverter: 35 regex='\d{2}' # 属性名必须为regex 36 37 def to_python(self, value): 38 return int(value) 39 40 def to_url(self, value): 41 return value # 匹配的regex是两个数字,返回的结果也必须是两个数字 42 43 44 from django.urls import path,register_converter 45 from app01.path_converts import MonthConverter 46 47 # 先注册转换器 48 register_converter(MonthConverter,'mon') 49 50 from app01 import views 51 52 53 urlpatterns = [ 54 path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'), 55 56 ] 57 58 59 4.模型层里面1.X外键默认都是级联更新删除的 60 但是到了2.X和3.X中需要你自己手动配置参数 61 models.ForeignKey(to='Publish') 62 63 models.ForeignKey(to='Publish',on_delete=models.CASCADE...) 64 """
四、视图层
三板斧解析
1 """ 2 HttpResponse 3 返回字符串类型 4 render 5 返回html页面 并且在返回给浏览器之前还可以给html文件传值 6 redirect 7 重定向 8 """ 9 # 视图函数必须要返回一个HttpResponse对象 正确 研究三者的源码即可得处结论 10 The view app01.views.index didn't return an HttpResponse object. It returned None instead. 11 12 # render简单内部原理 13 from django.template import Template,Context 14 res = Template('<h1>{{ user }}</h1>') 15 con = Context({'user':{'username':'jason','password':123}}) 16 ret = res.render(con) 17 print(ret) 18 return HttpResponse(ret) 19 20 ''' 21 总结: 22 render和redirect本质上都是HttpResponse对象 23 '''
JsonResponse对象
1 '''JsonResponse默认只能序列化字典 序列化其他需要加safe参数 需要指定safe=false''' 2 3 """ 4 json格式的数据有什么用? 5 前后端数据交互需要使用到json作为过渡 实现跨语言传输数据 6 7 前端序列化 8 JSON.stringify() json.dumps() 9 JSON.parse() json.loads() 10 """ 11 import json 12 from django.http import JsonResponse 13 def ab_json(request): 14 user_dict = {'username':'jason好帅哦,我好喜欢!','password':'123','hobby':'girl'} 15 16 l = [111,222,333,444,555] 17 # 先转成json格式字符串 18 # json_str = json.dumps(user_dict,ensure_ascii=False) 19 # 将该字符串返回 20 # return HttpResponse(json_str) 21 # 读源码掌握用法 22 # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False}) 23 # In order to allow non-dict objects to be serialized set the safe parameter to False. 24 # return JsonResponse(l,safe=False) 25 # 默认只能序列化字典 序列化其他需要加safe参数 26 27 '''JsonResponse默认只能序列化字典 序列化其他需要加safe参数'''
form表单上传文件及后端如何操作
1 """ 2 form表单上传文件类型的数据 3 1.method必须指定成post 4 2.enctype必须换成formdata 5 6 """ 7 def ab_file(request): 8 if request.method == 'POST': 9 # print(request.POST) # 只能获取普通的k:v键值对数据 文件不行 10 print(request.FILES) # 获取文件数据 11 # <MultiValueDict: {'file': [<InMemoryUploadedFile: u=1288812541,1979816195&fm=26&gp=0.jpg (image/jpeg)>]}> 12 file_obj = request.FILES.get('file') # 文件对象 13 print(file_obj.name) 14 with open(file_obj.name,'wb') as f: 15 for line in file_obj.chunks(): # 推荐加上chunks方法 其实跟不加是一样的都是一行行读取 16 f.write(line) 17 18 return render(request,'form.html')
request对象方法
1 """ 2 request.method 3 request.POST 4 request.GET 5 request.FILES 6 request.body # 原生的浏览器发过来的二进制数据 后面详细的讲 7 request.path 8 request.path_info 9 request.get_full_path() 能过获取完整的url及问号后面的参数 10 """ 11 print(request.path) # /app01/ab_file/ 12 print(request.path_info) # /app01/ab_file/ 13 print(request.get_full_path()) # /app01/ab_file/?username=jason 14
BV与CBV
1 # 视图函数既可以是函数也可以是类 2 def index(request): 3 return HttpResponse('index') 4 5 # CBV 6 # CBV路由 7 url(r'^login/',views.MyLogin.as_view()) 8 9 10 from django.views import View 11 12 class MyLogin(View): 13 def get(self,request): 14 return render(request,'form.html') 15 16 def post(self,request): 17 return HttpResponse('post方法') 18 19 """ 20 FBV和CBV各有千秋 21 CBV特点 22 能够直接根据请求方式的不同直接匹配到对应的方法执行 23 24 内部到底是怎么实现的? 25 CBV内部源码(******) 26 """
CBV源码剖析
1 # 你自己不要修改源码 除了bug很难找 2 3 # 突破口在urls.py 4 url(r'^login/',views.MyLogin.as_view()) 5 # url(r'^login/',views.view) FBV一模一样 6 # CBV与FBV在路由匹配上本质是一样的 都是路由 对应 函数内存地址 7 8 """ 9 函数名/方法名 加括号执行优先级最高 10 猜测 11 as_view() 12 要么是被@staicmethod修饰的静态方法 13 要么是被@classmethod修饰的类方法 正确 14 15 @classonlymethod 16 def as_view(cls, **initkwargs): 17 pass 18 """ 19 20 @classonlymethod 21 def as_view(cls, **initkwargs): 22 """ 23 cls就是我们自己写的类 MyCBV 24 Main entry point for a request-response process. 25 """ 26 def view(request, *args, **kwargs): 27 self = cls(**initkwargs) # cls是我们自己写的类 28 # self = MyLogin(**initkwargs) 产生一个我们自己写的类的对象 29 return self.dispatch(request, *args, **kwargs) 30 """ 31 以后你们会经常需要看源码 但是在看python源码的时候 一定要时刻提醒自己面向对象属性方法查找顺序 32 先从对象自己找 33 再去产生对象的类里面找 34 之后再去父类找 35 ... 36 总结:看源码只要看到了self点一个东西 一定要问你自己当前这个self到底是谁 37 """ 38 return view 39 40 # CBV的精髓 41 def dispatch(self, request, *args, **kwargs): 42 # 获取当前请求的小写格式 然后比对当前请求方式是否合法 43 # get请求为例 44 # post请求 45 if request.method.lower() in self.http_method_names: 46 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) 47 """ 48 反射:通过字符串来操作对象的属性或者方法 49 handler = getattr(自己写的类产生的对象,'get',当找不到get属性或者方法的时候就会用第三个参数) 50 handler = 我们自己写的类里面的get方法 51 """ 52 else: 53 handler = self.http_method_not_allowed 54 return handler(request, *args, **kwargs) 55 """ 56 自动调用get方法 57 """ 58 59 # 要求掌握到不看源码也能够描述出CBV的内部执行流程(******)
五、模板层
模版语法传值两种方式
1.{{}}:变量相关 2.{%%}:逻辑相关、导入各种功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def index(request): 2 # 模版语法可以传递的后端python数据类型 3 n = 123 4 f = 11.11 5 s = '我也想奔现' 6 b = True 7 l = ['小红','姗姗','花花','茹茹'] 8 t = (111,222,333,444) 9 d = {'username':'jason','age':18,'info':'这个人有点意思'} 10 se = {'晶晶','洋洋','嘤嘤'} 11 12 def func(): 13 print('我被执行了') 14 return '你的另一半在等你' 15 16 class MyClass(object): 17 def get_self(self): 18 return 'self' 19 20 @staticmethod 21 def get_func(): 22 return 'func' 23 24 @classmethod 25 def get_class(cls): 26 return 'cls' 27 28 # 对象被展示到html页面上 就类似于执行了打印操作 也会触发__str__方法 29 def __str__(self): 30 return '到底会不会?' 31 32 obj = MyClass() 33 hhh = '<h1>我是谁我在哪</h1>' 34 sss = '<script>alert(123)</script>' 35 36 from django.utils.safestring import mark_safe 37 res = mark_safe('<h1>新新</h1>') # 后端写好格式传给前端 38 39 # return render(request,'index.html',{}) # 一个个传 40 return render(request,'index.html',locals()) 41 42 43 <p>{{ n }}</p> 44 <p>{{ f }}</p> 45 <p>{{ s }}</p> 46 <p>{{ b }}</p> 47 <p>{{ l }}</p> 48 <p>{{ d }}</p> 49 <p>{{ t }}</p> 50 <p>{{ se }}</p> 51 <p>传递函数名会自动加括号调用 但是模版语法不支持给函数传额外的参数:{{ func }}</p> 52 <p>传类名的时候也会自动加括号调用(实例化){{ MyClass }}</p> 53 <p>内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行 针对的是函数名和类名</p> 54 <p>{{ obj }}</p> 55 <p>{{ obj.get_self }}</p> 56 <p>{{ obj.get_func }}</p> 57 <p>{{ obj.get_class }}</p> 58 59 60 # django模版语法的取值 是固定的格式 只能采用“句点符” . 61 <p>{{ d.username }}</p> 62 <p>{{ l.0 }}</p> 63 <p>{{ d.hobby.3.info }}</p> 64 # 即可以点键也可以点索引 还可以两者混用
过滤器(过滤器只能最多有两个参数)
1 # 过滤器就类似于是模版语法内置的 内置方法 2 # django内置有60多个过滤器 我们不需要学这么多 了解10个左右就差不多了 后面碰到了再去记忆 3 4 5 6 # 基本语法 7 {{数据|过滤器:参数}} 8 9 # 转义 10 # 前端 11 |safe 12 # 后端 13 from django.utils.safestring import mark_safe 14 res = mark_safe('<h1>新新</h1>') 15 """ 16 以后你在全栈项目的时候 前端代码不一定非要在前端页面书写 17 也可以现在先在后端写好 然后传递给前端页面 18 """ 19 20 21 <h1>过滤器</h1> 22 <p>统计长度:{{ s|length }}</p> 23 <p>默认值(第一个参数布尔值是True就展示第一个参数的值否在展示冒号后面的值):{{ b|default:'啥也不是' }}</p> 24 <p>文件大小:{{ file_size|filesizeformat }}</p> 25 <p>日期格式化:{{ current_time|date:'Y-m-d H:i:s' }}</p> 26 <p>切片操作(支持步长):{{ l|slice:'0:4:2' }}</p> 27 <p>切取字符(包含三个点):{{ info|truncatechars:9 }}</p> 28 <p>切取单词(不包含三个点 按照空格切):{{ egl|truncatewords:9 }}</p> 29 <p>切取单词(不包含三个点 按照空格切):{{ info|truncatewords:9 }}</p> 30 <p>移除特定的字符:{{ msg|cut:' ' }}</p> 31 <p>拼接操作:{{ l|join:'$' }}</p> 32 <p>拼接操作(加法):{{ n|add:10 }}</p> 33 <p>拼接操作(加法):{{ s|add:msg }}</p> 34 <p>转义:告诉浏览器,前面的标签是安全的,直接按照html格式转换{{ hhh|safe}}</p> 35 <p>转义:告诉浏览器,前面的标签是安全的,直接按照html格式转换{{ sss|safe }}</p> 36 <p>转义:后端代码写好格式传给前端{{ res }}</p> 37 38
标签
1 ''' 2 标签就是一堆逻辑 3 语法:{%标签名 参数1 参数2... %} # 参数之间用空格隔开 4 5 ''' 6 7 # for循环 8 {% for foo in l %} 9 <p>{{ forloop }}</p> 10 <p>{{ foo }}</p> 一个个元素 11 {% endfor %} 12 forloop:{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 6, 'revcounter0': 5, 'first': True, 'last': False} 13 14 # if判断 15 {% if b %} 16 <p>baby</p> 17 {% elif s%} 18 <p>都来把</p> 19 {% else %} 20 <p>老baby</p> 21 {% endif %} 22 23 24 # for与if混合使用 25 {% for foo in lll %} 26 {% if forloop.first %} 27 <p>这是我的第一次</p> 28 {% elif forloop.last %} 29 <p>这是最后一次啊</p> 30 {% else %} 31 <p>{{ foo }}</p> 32 {% endif %} 33 {% empty %} 34 <p>for循环的可迭代对象内部没有元素 根本没法循环</p> 35 {% endfor %} 36 37 38 39 # 处理字典其他方法 40 {% for foo in d.keys %} 41 <p>{{ foo }}</p> 42 {% endfor %} 43 {% for foo in d.values %} 44 <p>{{ foo }}</p> 45 {% endfor %} 46 {% for foo in d.items %} 47 <p>{{ foo }}</p> 48 {% endfor %} 49 50 51 # with起别名 52 {% with d.hobby.3.info as nb %} 53 <p>{{ nb }}</p> 54 在with语法内就可以通过as后面的别名快速的使用到前面非常复杂获取数据的方式 55 <p>{{ d.hobby.3.info }}</p> 56 {% endwith %}
定义过滤器、标签、inclusion_tag
1 """ 2 先三步走 3 1.在应用下创建一个名字”必须“叫templatetags文件夹 4 2.在该文件夹内创建“任意”名称的py文件 eg:mytag.py 5 3.在该py文件内"必须"先书写下面两句话(单词一个都不能错) 6 from django import template 7 8 register = template.Library() 9 """ 10 11 # 自定义过滤器 12 @register.filter(name='baby') # 如果不取name的名字就会用my_sum作为过滤器名字 13 def my_sum(v1, v2): 14 return v1 + v2 15 # 使用 16 {% load mytag %} 17 <p>{{ n|baby:666 }}</p> 18 19 20 # 自定义标签(参数可以有多个) 类似于自定义函数 21 @register.simple_tag(name='plus') 22 def index(a,b,c,d): 23 return '%s-%s-%s-%s'%(a,b,c,d) 24 # 使用 25 标签多个参数彼此之间空格隔开 26 <p>{% plus 'jason' 123 123 123 %}</p> 27 28 29 # 自定义inclusion_tag 30 """ 31 内部原理 32 先定义一个方法 33 在页面上调用该方法 并且可以传值 34 该方法会生成一些数据然后传递给一个html页面 35 之后将渲染好的结果放到调用的位置 36 """ 37 @register.inclusion_tag('left_menu.html') 38 def left(n): 39 data = ['第{}项'.format(i) for i in range(n)] 40 # 第一种 41 # return {'data':data} # 将data传递给left_menu.html 42 # 第二种 43 return locals() # 将data传递给left_menu.html 44 # left_menu.html: 45 <ul> 46 {% for foo in data %} 47 <li>{{ foo }}</li> 48 {% endfor %} 49 </ul> 50 51 # index.html前端页面: 52 {% left 5 %} 53 # 总结:当html页面某一个地方的页面需要传参数才能够动态的渲染出来,并且在多个页面上都需要使用到该局部 那么就考虑将该局部页面做成inclusion_tag形式 54 (在讲bbs的时候会使用到)
模版的继承
1 """ 2 你们有没有见过一些网站 3 这些网站页面整体都大差不差 只是某一些局部在做变化 4 """ 5 # 模版的继承 你自己先选好一个你要想继承的模版页面 6 {% extends 'home.html' %} 7 8 # 继承了之后子页面跟模版页面长的是一模一样的 你需要在模版页面上提前划定可以被修改的区域 9 {% block content %} 10 模版内容 11 {% endblock %} 12 13 # 子页面就可以声明想要修改哪块划定了的区域 14 {% block content %} 15 子页面内容 16 {% endblock %} 17 18 19 # 一般情况下模版页面上应该至少有三块可以被修改的区域 20 1.css区域 21 2.html区域 22 3.js区域 23 {% block css %} 24 25 {% endblock %} 26 27 {% block content %} 28 29 {% endblock %} 30 31 {% block js %} 32 33 {% endblock %} 34 # 每一个子页面就都可以有自己独有的css代码 html代码 js代码 35 36 """ 37 一般情况下 模版的页面上划定的区域越多 那么该模版的扩展性就越高 38 但是如果太多 那还不如自己直接写 39 """
模版的导入
1 """ 2 将页面的某一个局部当成模块的形式 3 哪个地方需要就可以直接导入使用即可 4 """ 5 {% include '模板文件名' %} 6 {% include 'wasai.html' %}
六、模型层
1.单表查询
1 # django自带的sqlite3数据库对日期格式不是很敏感 处理的时候容易出错 2 ''' 3 总结: 4 1.增: 5 1. models.类名.objects.create(字段名='xxx',字段名1='xxx') 6 2. obj = models.类名(字段名='xxx',字段名1='xxx') 7 obj.save() 8 9 2.删: 10 models.类名.objects.filter(条件).delete() 11 12 3.改: 13 modes.类名.objects.filter(条件).update(字段名='xxx',字段名1='xxx') 14 15 ''' 16 17 18 # 增 19 # res = models.User.objects.create(name='jason',age=18,register_time='2002-1-21') 20 # print(res) 21 # import datetime 22 # ctime = datetime.datetime.now() 23 # user_obj = models.User(name='egon',age=84,register_time=ctime) 24 # user_obj.save() 25 26 # 删 27 # res = models.User.objects.filter(pk=2).delete() 28 # print(res) 29 """ 30 pk会自动查找到当前表的主键字段 指代的就是当前表的主键字段primary key 31 用了pk之后 你就不需要指代当前表的主键字段到底叫什么了 32 uid 33 pid 34 sid 35 ... 36 """ 37 # user_obj = models.User.objects.filter(pk=1).first() 38 # user_obj.delete() 39 40 # 修改 41 # models.User.objects.filter(pk=4).update(name='egonDSB') 42 43 # user_obj = models.User.objects.get(pk=4) # get返回的直接就是数据对象 44 # user_obj = models.User.objects.filter(pk=6) # 而filter返回的是QuerySet对象 45 """ 46 get方法返回的直接就是当前数据对象 而filter返回的是QuerySet对象 47 但是该方法不推荐使用get方法 因为一旦数据不存在该方法会直接报错 48 而filter则不会 所以我们还是用filter 49 """ 50 # user_obj.name = 'egonPPP' 51 # user_obj.save() 52
前期表的准备
1 ''' 2 书跟出版社是一对多关系,一个出版社可以出版多本书,一本书只能由一个出版社出版(版权) 3 书跟作者是多对多关系,一个作者可以写多本书,一本书也可以由多个作者联合出版 4 作者跟作者详情表是一对一关系,一个作者对应一个作者详情 5 ''' 6 7 class User(models.Model): 8 name = models.CharField(max_length=32, verbose_name='姓名') 9 age = models.IntegerField(verbose_name='年龄') 10 register_time = models.DateField(verbose_name='注册时间') 11 12 13 def __str__(self): 14 return '对象:%s'%self.name 15 16 class Book(models.Model): 17 title = models.CharField(max_length=32,verbose_name='书名') 18 price = models.DecimalField(max_digits=8,decimal_places=2, verbose_name='书价格') 19 publish_time = models.DateField(auto_now_add=True, verbose_name='出版时间') 20 sell_num = models.IntegerField(default=1000,verbose_name='卖出的书') 21 left_num = models.IntegerField(default=1000,verbose_name='剩下的库存') 22 # 一对多 外键 23 publish = models.ForeignKey(to='Publish') 24 # 多对多 25 authors = models.ManyToManyField(to='Author') 26 27 def __str__(self): 28 return '对象:%s'%self.title 29 30 31 class Publish(models.Model): 32 name = models.CharField(max_length=32, verbose_name='出版社') 33 addr = models.CharField(max_length=64, verbose_name='出版社地址') 34 email = models.EmailField() # varchar(254) 35 36 def __str__(self): 37 return '对象:%s'%self.name 38 39 40 class Author(models.Model): 41 name = models.CharField(max_length=32, verbose_name='作者') 42 age = models.IntegerField(verbose_name='作者年龄') 43 44 # 一对一 45 author_detail = models.OneToOneField(to='AuthorDetail') 46 47 def __str__(self): 48 return '对象:%s'%self.name 49
必知必会13条
1 # 必知必会13条 2 # 1.all() # 查询所有数据 3 # 2.filter() # 带有过滤条件的查询 4 # 3.get() # 直接拿数据对象 但是条件不存在直接报错 5 # 4.first() # 拿queryset里面第一个元素 6 # res = models.User.objects.all().first() 7 # print(res) 8 # 5.last() # 拿queryset里面最后一个元素 9 # res = models.User.objects.all().last() 10 # print(res) 11 # 6.values() # 可以指定获取的数据字段 select name,age from ... 列表套字典 12 # res = models.User.objects.values('name','age') # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]> 13 # print(res) 14 # 7.values_list() 列表套元组 15 # res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]> 16 # print(res) 17 """ 18 res.query查看内部封装的sql语句 19 上述查看sql语句的方式 只能用于queryset对象 20 只有queryset对象才能够点击query查看内部的sql语句 21 """ 22 23 # 8.distinct() 去重 24 # res = models.User.objects.values('name','age').distinct() 25 # print(res) 26 """ 27 去重一定要是一模一样的数据 28 如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键 29 """ 30 31 # 9.order_by() 32 # res = models.User.objects.order_by('age') # 默认升序 33 # res = models.User.objects.order_by('-age') # 降序 34 # print(res) 35 36 # 10.reverse() 反转的前提是 数据已经排过序了 order_by() 37 # res = models.User.objects.all() 38 # res1 = models.User.objects.order_by('age').reverse() 39 # print(res,res1) 40 41 # 11.count() 统计当前数据的个数 42 # res = models.User.objects.count() 43 # print(res) 44 45 # 12.exclude() 排除在外 46 # res = models.User.objects.exclude(name='jason') 47 # print(res) 48 49 # 13.exists() 基本用不到因为数据本身就自带布尔值 返回的是布尔值 50 # res = models.User.objects.filter(pk=10).exists() 51 # print(res) 52
测试脚本
1 """ 2 当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式 3 而是直接写一个测试脚本即可 4 5 脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以 6 """ 7 # 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写import django和django.setup() 8 9 import os 10 import sys 11 12 if __name__ == "__main__": 13 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings") 14 import django 15 django.setup() 16 # 在这个代码块的下面就可以测试django里面的单个py文件了 17
查看内部sql语句的方式
1 # 方式1 2 res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]> 3 print(res.query) 4 queryset对象才能够点击query查看内部的sql语句 5 6 # 方式2:所有的sql语句都能查看 7 # 去配置文件中配置一下即可 8 LOGGING = { 9 'version': 1, 10 'disable_existing_loggers': False, 11 'handlers': { 12 'console':{ 13 'level':'DEBUG', 14 'class':'logging.StreamHandler', 15 }, 16 }, 17 'loggers': { 18 'django.db.backends': { 19 'handlers': ['console'], 20 'propagate': True, 21 'level':'DEBUG', 22 }, 23 } 24 }
神奇的双下划线查询(相当于SQL语句中的where筛选)
1 # 神奇的双下划线查询 2 # 1 年龄大于35岁的数据 3 # res = models.User.objects.filter(age__gt=35) 4 # print(res) 5 6 # 2 年龄小于35岁的数据 7 # res = models.User.objects.filter(age__lt=35) 8 # print(res) 9 # 大于等于 小于等于 10 # res = models.User.objects.filter(age__gte=32) 11 # print(res) 12 # res = models.User.objects.filter(age__lte=32) 13 # print(res) 14 15 # 年龄是18 或者 32 或者40 16 # res = models.User.objects.filter(age__in=[18,32,40]) 17 # print(res) 18 19 # 年龄在18到40岁之间的 首尾都要 20 # res = models.User.objects.filter(age__range=[18,40]) 21 # print(res) 22 23 # 查询出名字里面含有s的数据 模糊查询 24 # res = models.User.objects.filter(name__contains='s') 25 # print(res) 26 27 # 是否区分大小写 查询出名字里面含有p的数据 区分大小写 28 # res = models.User.objects.filter(name__contains='p') 29 # print(res) 30 # 忽略大小写 31 # res = models.User.objects.filter(name__icontains='p') 32 # print(res) 33 34 # res = models.User.objects.filter(name__startswith='j') 35 # res1 = models.User.objects.filter(name__endswith='j') 36 # 37 # print(res,res1) 38 39 40 # 查询出注册时间是 2020 1月 41 # res = models.User.objects.filter(register_time__month='1') 42 # res = models.User.objects.filter(register_time__year='2020') 43
一对多外键增删改查
1 # 一对多外键增删改查 2 3 # 增 4 # 1 直接写实际字段 id 5 # models.Book.objects.create(title='论语',price=899.23,publish_id=1) 6 # models.Book.objects.create(title='聊斋',price=444.23,publish_id=2) 7 # models.Book.objects.create(title='老子',price=333.66,publish_id=1) 8 # 2 虚拟字段 对象 9 # publish_obj = models.Publish.objects.filter(pk=2).first() 10 # models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj) 11 12 # 删 13 # models.Publish.objects.filter(pk=1).delete() # 级联删除 14 15 # 修改 16 # models.Book.objects.filter(pk=1).update(publish_id=2) 17 # publish_obj = models.Publish.objects.filter(pk=1).first() 18 # models.Book.objects.filter(pk=1).update(publish=publish_obj) 19 ''' 20 外键字段可以用外键id 也可以用外键对应的字段对象 21 详情见上面create/update中的publish_id和publish的区别 22 ''' 23
多对多外键增删改查
1 # 如何给书籍添加作者? 2 book_obj = models.Book.objects.filter(pk=1).first() 3 # print(book_obj.authors) # 就类似于你已经到了第三张关系表了 4 # book_obj.authors.add(1) # 书籍id为1的书籍绑定一个主键为1 的作者 5 # book_obj.authors.add(2,3) 6 7 # author_obj = models.Author.objects.filter(pk=1).first() 8 # author_obj1 = models.Author.objects.filter(pk=2).first() 9 # author_obj2 = models.Author.objects.filter(pk=3).first() 10 # book_obj.authors.add(author_obj) 11 # book_obj.authors.add(author_obj1,author_obj2) 12 """ 13 add给第三张关系表添加数据 14 括号内既可以传数字也可以传对象 并且都支持多个 15 """ 16 17 # 删 18 # book_obj.authors.remove(2) 19 # book_obj.authors.remove(1,3) 20 21 # author_obj = models.Author.objects.filter(pk=2).first() 22 # author_obj1 = models.Author.objects.filter(pk=3).first() 23 # book_obj.authors.remove(author_obj,author_obj1) 24 """ 25 remove 26 括号内既可以传数字也可以传对象 并且都支持多个 27 """ 28 29 30 # 修改 31 # book_obj.authors.set([1,2]) # 括号内必须给一个可迭代对象 32 # book_obj.authors.set([3]) # 括号内必须给一个可迭代对象 33 34 # author_obj = models.Author.objects.filter(pk=2).first() 35 # author_obj1 = models.Author.objects.filter(pk=3).first() 36 # book_obj.authors.set([author_obj,author_obj1]) # 括号内必须给一个可迭代对象 37 38 """ 39 set 40 括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个 41 如果括号内的的数字/对象存在则不修改, 42 如果不存在则把其他的删除后再添加括号内的对应的数字/对象 43 44 """ 45 46 47 # 清空 48 # 在第三张关系表中清空某个书籍与作者的绑定关系 49 book_obj.authors.clear() 50 """ 51 clear 52 括号内不要加任何参数 53 54 """ 55
正反向的概念
1 # 正向 2 # 反向 3 外键字段在我手上那么,我查你就是正向 4 外键字段如果不在手上,我查你就是反向 5 6 book >>>外键字段在书那儿(正向)>>> publish 7 publish >>>外键字段在书那儿(反向)>>>book 8 9 # 一对一和多对多正反向的判断也是如此 10 11 """ 12 查询口诀: 13 正向查询按外键字段 14 反向查询按表名小写_set 15 """
2.多表查询
基于对象的跨表查询(子查询)
1 """ 2 子查询就是分步查询 3 4 查询口诀: 5 正向查询按外键字段 如果有多个需要加all() 6 反向查询按表名小写 如果有多个需要加_set.all() 7 """ 8 9 基于对象的跨表查询 10 11 # 1.查询书籍主键为1的出版社 12 # book_obj = models.Book.objects.filter(pk=1).first() 13 # # 书查出版社 正向 14 # res = book_obj.publish 15 # print(res) 16 # print(res.name) 17 # print(res.addr) 18 19 # 2.查询书籍主键为2的作者 20 # book_obj = models.Book.objects.filter(pk=2).first() 21 # # 书查作者 正向 22 # # res = book_obj.authors # app01.Author.None 23 # res = book_obj.authors.all() # <QuerySet [<Author: Author object>, <Author: Author object>]> 24 # 25 # print(res) 26 27 # 3.查询作者jason的电话号码 28 # author_obj = models.Author.objects.filter(name='jason').first() 29 # res = author_obj.author_detail 30 # print(res) 31 # print(res.phone) 32 # print(res.addr) 33 34 """ 35 在书写orm语句的时候跟写sql语句一样的 36 不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点 37 38 正向什么时候需要加.all() 39 当你的结果可能有多个的时候就需要加.all() 40 如果是一个则直接拿到数据对象 41 book_obj.publish 42 book_obj.authors.all() 43 author_obj.author_detail 44 """ 45 # 4.查询出版社是东方出版社出版的书 46 # publish_obj = models.Publish.objects.filter(name='东方出版社').first() 47 # 出版社查书 反向 48 # res = publish_obj.book_set # app01.Book.None 49 # res = publish_obj.book_set.all() 50 # print(res) 51 52 # 5.查询作者是jason写过的书 53 # author_obj = models.Author.objects.filter(name='jason').first() 54 # 作者查书 反向 55 # res = author_obj.book_set # app01.Book.None 56 # res = author_obj.book_set.all() 57 # print(res) 58 59 # 6.查询手机号是110的作者姓名 60 # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first() 61 # res = author_detail_obj.author 62 # print(res.name) 63 """ 64 基于对象 65 反向查询的时候 66 当你的查询结果可以有多个的时候 就必须加_set.all() 67 当你的结果只有一个的时候 不需要加_set.all() 68 """ 69
基于双下划线的跨表查询(联表查询)
1 """ 2 联表查询就是一步查询 3 4 查询口诀: 5 正向查询按外键字段 6 反向查询按表名小写 7 8 正向联表:models.类名.objects.filter(条件).values('字段名__子字段名') 9 反向联表:models.类名.objects.filter(表名__字段名).values('条件') 10 """ 11 12 基于双下划线的跨表查询 13 14 15 # 1.查询jason的手机号和作者姓名 16 # res = models.Author.objects.filter(name='jason').values('author_detail__phone','name') 17 # print(res) 18 # 反向 19 # res = models.AuthorDetail.objects.filter(author__name='jason') # 拿作者姓名是jason的作者详情 20 # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name') 21 # print(res) 22 23 24 # 2.查询书籍主键为1的出版社名称和书的名称 25 # res = models.Book.objects.filter(pk=1).values('title','publish__name') 26 # print(res) 27 # 反向 28 # res = models.Publish.objects.filter(book__id=1).values('name','book__title') 29 # print(res) 30 31 # 3.查询书籍主键为1的作者姓名 32 # res = models.Book.objects.filter(pk=1).values('authors__name') 33 # print(res) 34 # 反向 35 # res = models.Author.objects.filter(book__id=1).values('name') 36 # print(res) 37 38 39 # 查询书籍主键是1的作者的手机号 40 # book author authordetail 41 # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') 42 # print(res) 43 """ 44 正向联表:models.类名.objects.filter(条件).values('字段名__子字段名') 45 反向联表:models.类名.objects.filter(表名__字段名).values('条件') 46 你只要掌握了正反向的概念 47 以及双下划线 48 那么你就可以无限制的跨表 49 50 """ 51
聚合查询
1 # 聚合查询 aggregate 2 """ 3 聚合查询通常情况下都是配合分组一起使用的 4 只要是跟数据库相关的模块 5 基本上都在django.db.models里面 6 如果上述没有那么应该在django.db里面 7 """ 8 from app01 import models 9 from django.db.models import Max,Min,Sum,Count,Avg 10 # 1 所有书的平均价格 11 # res = models.Book.objects.aggregate(Avg('price')) 12 # print(res) 13 # 2.上述方法一次性使用 14 res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price')) 15 print(res) 16
分组查询
1 # 分组查询 annotate 等价于MySQL中的group by 2 """ 3 MySQL分组查询都有哪些特点 4 分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了 5 严格模式 6 ONLY_FULL_GROUP_BY 7 8 """ 9 from django.db.models import Max, Min, Sum, Count, Avg 10 # 1.统计每一本书的作者个数 11 # res = models.Book.objects.annotate() # models后面点什么 就是按什么分组 12 # models.Book.objects.values('字段').annotate() # values出现在了annotate的前面,那么就按照values括号内指定的字段分组 13 # res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num') 14 """ 15 author_num是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数 16 """ 17 # res1 = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num') 18 # print(res,res1) 19 """ 20 代码没有补全 不要怕 正常写 21 补全给你是pycharm给你的 到后面在服务器上直接书写代码 什么补全都没有 颜色提示也没有 22 23 """ 24 25 # 2.统计每个出版社卖的最便宜的书的价格(作业:复习原生SQL语句 写出来) 26 # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price') 27 # print(res) 28 29 # 3.统计不止一个作者的图书 30 # 1.先按照图书分组 求每一本书对应的作者个数 31 # 2.过滤出不止一个作者的图书 32 # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num') 33 # """ 34 # 只要你的orm语句得出的结果还是一个queryset对象 35 # 那么它就可以继续无限制的点queryset对象封装的方法 36 # 37 # """ 38 # print(res) 39 40 # 4.查询每个作者出的书的总价格 41 # res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price') 42 # print(res) 43 44 """ 45 如果我想按照指定的字段分组该如何处理呢? 46 models.Book.objects.values('price').annotate() 47 后续BBS作业会使用 48 49 50 你们的机器上如果出现分组查询报错的情况 51 你需要修改数据库严格模式 52 """ 53
F与Q查询
1 # F查询 2 """ 3 F查询:能够帮助你直接获取到表中某个字段对应的数据 4 """ 5 # 1.查询卖出数大于库存数的书籍 6 # F查询 7 from django.db.models import F 8 # res = models.Book.objects.filter(sell_num__gt=F('left_num')) 9 # print(res) 10 11 12 # 2.将所有书籍的价格提升500块 13 # models.Book.objects.update(price=F('price') + 500) 14 15 16 # 3.将所有书的名称后面加上爆款两个字 17 """ 18 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接 19 """ 20 from django.db.models.functions import Concat 21 from django.db.models import Value 22 models.Book.objects.update(title=Concat(F('title'), Value('爆款'))) 23 # models.Book.objects.update(title=F('title') + '爆款') # 所有的名称会全部变成空白 24 25
django中如何开启事务
1 """ 2 事务 3 ACID 4 原子性 5 不可分割的最小单位 6 一致性 7 跟原子性是相辅相成 8 隔离性 9 事务之间互相不干扰 10 持久性 11 事务一旦确认永久生效 12 13 事务的回滚 14 rollback 15 事务的确认 16 commit 17 """ 18 # 目前你只需要掌握Django中如何简单的开启事务 19 # 事务 20 from django.db import transaction 21 try: 22 with transaction.atomic(): 23 # sql1 24 # sql2 25 ... 26 # 在with代码快内书写的所有orm操作都是属于同一个事务 27 except Exception as e: 28 print(e) 29 print('执行其他操作') 30 31
orm中常用字段及参数
1 AutoField 2 主键字段 primary_key=True 3 4 CharField varchar 5 verbose_name 字段的注释 6 max_length 长度 7 8 IntegerField int 9 BigIntegerField bigint 10 11 DecimalField 12 max_digits=8 13 decimal_places=2 14 15 EmailFiled varchar(254) 16 17 DateField date 18 DateTimeField datetime 19 auto_now:每次修改数据的时候都会自动更新当前时间 20 auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了 21 22 BooleanField(Field) - 布尔值类型 23 该字段传布尔值(False/True) 数据库里面存0/1 24 25 TextField(Field) - 文本类型 26 该字段可以用来存大段内容(文章、博客...) 没有字数限制 27 后面的bbs作业 文章字段用的就是TextField 28 29 30 FileField(Field) - 字符类型 31 upload_to = "/data" 32 给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中 33 /data/a.txt 34 后面bbs作业也会涉及 35 36 # 更多字段 37 直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html 38 39 40 # django除了给你提供了很多字段类型之外 还支持你自定义字段 41 class MyCharField(models.Field): 42 def __init__(self,max_length,*args,**kwargs): 43 self.max_length = max_length 44 # 调用父类的init方法 45 super().__init__(max_length=max_length,*args,**kwargs) # 一定要是关键字的形式传入 46 47 def db_type(self, connection): 48 """ 49 返回真正的数据类型及各种约束条件 50 :param connection: 51 :return: 52 """ 53 return 'char(%s)'%self.max_length 54 55 # 自定义字段使用 56 myfield = MyCharField(max_length=16,null=True) 57 58 59 60 # 外键字段及参数 61 unique=True 62 ForeignKey(unique=True) === OneToOneField() 63 # 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用 64 65 db_index 66 如果db_index=True 则代表着为此字段设置索引 67 (复习索引是什么) 68 69 to_field 70 设置要关联的表的字段 默认不写关联的就是另外一张的主键字段 71 72 on_delete 73 当删除关联表中的数据时,当前表与其关联的行的行为。 74 """ 75 django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除 76 """
数据库查询优化
1 only与defer 2 select_related与prefetch_related 3 4 """ 5 orm语句的特点: 6 惰性查询 7 如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数 8 那么orm会自动识别 直接不执行 9 """ 10 # only与defer 11 # res = models.Book.objects.all() 12 # print(res) # 要用数据了才会走数据库 13 14 # 想要获取书籍表中所有数的名字 15 # res = models.Book.objects.values('title') 16 # for d in res: 17 # print(d.get('title')) 18 # 你给我实现获取到的是一个数据对象 然后点title就能够拿到书名 并且没有其他字段 19 # res = models.Book.objects.only('title') 20 # res = models.Book.objects.all() 21 # print(res) # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>, <Book: 论语爆款>, <Book: 聊斋爆款>, <Book: 老子爆款>]> 22 # for i in res: 23 # print(i.title) # 点击only括号内的字段 不会走数据库 24 # print(i.price) # 点击only括号内没有的字段 会重新走数据库查询而all不需要走了 25 26 res = models.Book.objects.defer('title') # 对象除了没有title属性之外其他的都有 27 for i in res: 28 print(i.price) 29 """ 30 defer与only刚好相反 31 defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据 32 而如果查询的是非括号内的字段 则不需要走数据库了 33 34 """ 35 ''' 36 总结: 37 only与defer的区别: 38 only查括号内放的字段 不需要再走数据库 而查不在括号内的字段,则需要重新走数据库 39 defer查括号内的字段需要重新走数据库 而查不在括号内的字段,不需要重新走数据库 40 所以这两者刚好相反 41 ''' 42 43 # select_related与prefetch_related 44 # select_related与prefetch_related 跟跨表操作有关 45 # res = models.Book.objects.all() 46 # for i in res: 47 # print(i.publish.name) # 每循环一次就要走一次数据库查询 48 49 # res = models.Book.objects.select_related('authors') # INNER JOIN 50 """ 51 select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据 52 全部封装给查询出来的对象 53 这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了 54 55 select_related括号内只能放外键字段 一对多 一对一 56 多对多也不行 57 括号内可以放多个外键字段 58 select_related(外键字段1__外键字段2__外键字段3__...) 59 """ 60 # for i in res: 61 # print(i.publish.name) # 每循环一次就要走一次数据库查询 62 63 res = models.Book.objects.prefetch_related('publish') # 子查询 64 """ 65 prefetch_related该方法内部其实就是子查询 66 将子查询查询出来的所有结果也给你封装到对象中 67 给你的感觉好像也是一次性搞定的 68 """ 69 for i in res: 70 print(i.publish.name) 71 ''' 72 总结: 73 select_related 和 prefetch_related 的区别: 74 select_related:方法内部其实就是联表查询 75 prefetch_related:方法内部其实就是子查询 76 两者查询优势具体要看情况,如果表数据很多时,联表可能需要花费的时间比子查询花费的时间更多 77 ''' 78
图书管理之图书的增删改查
models.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # models.py 2 3 from django.db import models 4 5 # Create your models here. 6 7 class Book(models.Model): 8 title = models.CharField(max_length=32, verbose_name='书名') 9 price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格') 10 publish_time = models.DateField(auto_now_add=True, verbose_name='出版时间') 11 # 一对多 外键 书和出版社 12 publish = models.ForeignKey(to='Publish') 13 # 多对多 书和作者 14 authors = models.ManyToManyField(to='Author') 15 16 def __str__(self): 17 return '%s' % self.title 18 19 20 class Publish(models.Model): 21 name = models.CharField(max_length=32, verbose_name='出版社') 22 addr = models.CharField(max_length=64, verbose_name='地址') 23 email = models.EmailField(verbose_name='邮箱') 24 25 def __str(self): 26 return '%s' % self.name 27 28 class Author(models.Model): 29 name = models.CharField(max_length=32, verbose_name='作者名') 30 age = models.IntegerField(verbose_name='年龄') 31 # 一对一 外键 作者和作者详情表 32 author_detail = models.OneToOneField(to='AuthorDetail') 33 34 def __str__(self): 35 return '%s' % self.name 36 37 38 class AuthorDetail(models.Model): 39 phone = models.BigIntegerField(verbose_name='电话') 40 addr = models.CharField(max_length=64, verbose_name='地址')
urls.py
1 # urls.py 2 3 from django.conf.urls import url 4 from django.contrib import admin 5 from app01 import views 6 7 urlpatterns = [ 8 url(r'^admin/', admin.site.urls), 9 # 首页 10 url(r'^$', views.home), 11 # 图书列表 12 url(r'^book/list/', views.book_list, name='book_list'), 13 # 添加书籍 14 url(r'^book/add/', views.book_add, name='book_add'), 15 # 编辑书籍 16 url(r'^book/edit/(?P<edit_id>\d+)', views.book_edit, name='book_edit'), 17 # 删除页面 18 url(r'^book/delete/(\d+)', views.book_delete, name='book_delete') 19 ]
views.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # views.py 2 3 from django.shortcuts import render,HttpResponse,redirect,reverse 4 from app01 import models 5 6 # Create your views here. 7 8 9 def home(request): 10 return render(request,'home.html') 11 12 def book_list(request): 13 book_queryset = models.Book.objects.all() 14 return render(request,'book_list.html',locals()) 15 16 def book_add(request): 17 # 获取用户提交的数据 18 if request.method == 'POST': 19 title = request.POST.get('title') 20 price = request.POST.get('price') 21 publish_id = request.POST.get('publish') 22 author_list = request.POST.getlist('authors') # [1,2,3,4]获取的是一个列表 23 publish_time = request.POST.get('publish_time') 24 # 更新书籍信息数据库 25 # 下面的create可以返回一个书籍对象 26 book_obj = models.Book.objects.create(title=title,publish_id=publish_id,price=price,publish_time=publish_time) 27 # 通过多对多联表的外键添加操作 添加书籍与作者关系表 28 book_obj.authors.add(*author_list) # 刚好将列表数据打散 29 return redirect('book_list') 30 publish_queryset = models.Publish.objects.all() 31 author_queryset = models.Author.objects.all() 32 return render(request,'book_add.html',locals()) 33 34 35 def book_edit(request, edit_id): 36 book_obj = models.Book.objects.filter(pk=edit_id).first() 37 if request.method == 'POST': 38 title = request.POST.get('title') 39 price = request.POST.get('price') 40 publish_id = request.POST.get('publish') 41 publish_time = request.POST.get('publish_time') 42 author_list = request.POST.getlist('authors') 43 # 去数据库中更新书籍对象对应的数据 44 models.Book.objects.filter(pk=edit_id).update(title=title, price=price,publish_id=publish_id,publish_time=publish_time) 45 # 添加书籍表和作者表的对应关系 46 book_obj.authors.set(author_list) 47 return redirect('book_list') 48 publish_queryset = models.Publish.objects.all() 49 author_queryset = models.Author.objects.all() 50 return render(request, 'book_edit.html', locals()) 51 52 def book_delete(request,delete_id): 53 models.Book.objects.filter(pk=delete_id).delete() 54 return redirect('book_list')
home.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # home.html 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>Title</title> 8 {% load static %} 9 <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}"> 10 <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script> 11 <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> 12 {% block css %} 13 14 {% endblock %} 15 </head> 16 <body> 17 {#导航栏开始#} 18 <nav class="navbar navbar-inverse"> 19 <div class="container-fluid"> 20 <!-- Brand and toggle get grouped for better mobile display --> 21 <div class="navbar-header"> 22 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 23 <span class="sr-only">图书管理系统</span> 24 <span class="icon-bar"></span> 25 <span class="icon-bar"></span> 26 <span class="icon-bar"></span> 27 </button> 28 <a class="navbar-brand" href="#">图书</a> 29 </div> 30 31 <!-- Collect the nav links, forms, and other content for toggling --> 32 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 33 <ul class="nav navbar-nav"> 34 <li class="active"><a href="#">作者 <span class="sr-only">(current)</span></a></li> 35 <li><a href="#">作者详情</a></li> 36 <li class="dropdown"> 37 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多信息 <span class="caret"></span></a> 38 <ul class="dropdown-menu"> 39 <li><a href="#">Action</a></li> 40 <li><a href="#">Another action</a></li> 41 <li><a href="#">Something else here</a></li> 42 <li role="separator" class="divider"></li> 43 <li><a href="#">Separated link</a></li> 44 <li role="separator" class="divider"></li> 45 <li><a href="#">One more separated link</a></li> 46 </ul> 47 </li> 48 </ul> 49 <form class="navbar-form navbar-left"> 50 <div class="form-group"> 51 <input type="text" class="form-control" placeholder="输入作者/图书名"> 52 </div> 53 <button type="submit" class="btn btn-default">搜索</button> 54 </form> 55 <ul class="nav navbar-nav navbar-right"> 56 <li><a href="#">yesir</a></li> 57 <li class="dropdown"> 58 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">详细信息 <span class="caret"></span></a> 59 <ul class="dropdown-menu"> 60 <li><a href="#">Action</a></li> 61 <li><a href="#">Another action</a></li> 62 <li><a href="#">Something else here</a></li> 63 <li role="separator" class="divider"></li> 64 <li><a href="#">Separated link</a></li> 65 </ul> 66 </li> 67 </ul> 68 </div><!-- /.navbar-collapse --> 69 </div><!-- /.container-fluid --> 70 </nav> 71 {#导航栏结束#} 72 <div class="container-fluid"> 73 <div class="row"> 74 <div class="col-md-3"> 75 <div class="list-group"> 76 <a href="#" class="list-group-item active"> 77 图书管理系统 78 </a> 79 <a href="{% url 'book_list' %}" class="list-group-item">图书列表</a> 80 <a href="#" class="list-group-item">出版社列表</a> 81 <a href="#" class="list-group-item">作者列表</a> 82 <a href="#" class="list-group-item">作者详情列表</a> 83 </div> 84 </div> 85 <div class="col-md-9"> 86 {#面板开始#} 87 <div class="panel panel-primary"> 88 <div class="panel-heading">内容展示</div> 89 <div class="panel-body"> 90 {% block content %} 91 {#巨幕开始#} 92 <div class="jumbotron"> 93 <h1>欢迎来到我的图书馆</h1> 94 <p>更多详情请关注公众号</p> 95 <p><a class="btn btn-primary btn-lg" href="#" role="button">查看更多</a></p> 96 </div> 97 {#巨幕结束#} 98 {# 缩略图#} 99 <div class="col-sm-6 col-md-4"> 100 <div class="thumbnail"> 101 <img src="https://img0.baidu.com/it/u=3121212591,2014405310&fm=253&fmt=auto&app=138&f=JPEG?w=360&h=360" alt="..."> 102 <div class="caption"> 103 <h3>Thumbnail label</h3> 104 <p>查看详情</p> 105 <p><a href="#" class="btn btn-primary" role="button">添加</a> <a href="#" class="btn btn-default" role="button">删除</a></p> 106 </div> 107 </div> 108 </div> 109 <div class="col-sm-6 col-md-4"> 110 <div class="thumbnail"> 111 <img src="https://img2.baidu.com/it/u=4148471971,2557146461&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500" alt="..."> 112 <div class="caption"> 113 <h3>Thumbnail label</h3> 114 <p>查看详情</p> 115 <p><a href="#" class="btn btn-primary" role="button">添加</a> <a href="#" class="btn btn-default" role="button">删除</a></p> 116 </div> 117 </div> 118 </div> 119 <div class="col-sm-6 col-md-4"> 120 <div class="thumbnail"> 121 <img src="https://img2.baidu.com/it/u=535790725,1148347184&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500" alt="..."> 122 <div class="caption"> 123 <h3>Thumbnail label</h3> 124 <p>查看详情</p> 125 <p><a href="#" class="btn btn-primary" role="button">添加</a> <a href="#" class="btn btn-default" role="button">删除</a></p> 126 </div> 127 </div> 128 </div> 129 {# 缩略图结束#} 130 {% endblock %} 131 132 </div> 133 </div> 134 {#面板结束#} 135 </div> 136 </div> 137 </div> 138 {% block js %} 139 140 {% endblock %} 141 </body> 142 </html> 143 144 145 ************************************************************ 146 # book_list.html 147 148 {% extends 'home.html' %} 149 150 {% block content %} 151 <a href="{% url 'book_add' %}" class="btn btn-primary">添加</a> 152 <br> 153 <br> 154 <table class="table table-striped table-hover"> 155 <thead> 156 <tr> 157 <th>ID</th> 158 <th>书名</th> 159 <th>价格</th> 160 <th>作者</th> 161 <th>出版社</th> 162 <th>出版日期</th> 163 <th>操作</th> 164 </tr> 165 </thead> 166 <tbody> 167 {% for book_obj in book_queryset %} 168 <tr> 169 <td>{{ book_obj.pk }}</td> 170 <td>{{ book_obj.title }}</td> 171 <td>{{ book_obj.price }}</td> 172 <td> 173 {% for author_obj in book_obj.authors.all %} 174 {% if forloop.last %} 175 {{ author_obj.name }} 176 {% else %} 177 {{ author_obj.name }}、 178 {% endif %} 179 180 {% endfor %} 181 </td> 182 <td>{{ book_obj.publish.name }}</td> 183 <td>{{ book_obj.publish.publish_time|date:'Y-m-d' }}</td> 184 <td> 185 <a href="{% url 'book_edit' book_obj.pk %}" class="btn btn-primary btn-xs">编辑</a> 186 <a href="{% url 'book_delete' book_obj.pk %}" class="btn btn-danger btn-xs">删除</a> 187 </td> 188 </tr> 189 {% endfor %} 190 191 </tbody> 192 </table> 193 {% endblock %} 194 195 196 ************************************************************ 197 # book_edit.html 198 199 {% extends 'home.html' %} 200 201 202 {% block content %} 203 <h1 class="text-center">编辑书籍</h1> 204 <form action="" method="post"> 205 <p>书名<input type="text" name="title" value="{{ book_obj.title }}" class="form-control"></p> 206 <p>价格<input type="text" name="price" value="{{ book_obj.price }}" class="form-control"></p> 207 <p>出版社 208 <select name="publish" id="" class="form-control"> 209 {% for publish_obj in publish_queryset %} 210 {# 如果当前循环出的出版社对象等于编辑的书籍对应的出版社对象,则默认选中状态#} 211 {% if book_obj.publish == publish_obj %} 212 <option value="{{ publish_obj.pk }}" selected>{{ publish_obj.name }}</option> 213 {% else %} 214 <option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option> 215 {% endif %} 216 217 218 {% endfor %} 219 220 </select> 221 </p> 222 <p>出版时间<input type="date" name="publish_time" value="{{ book_obj.publish_time|date:'Y-m-d' }}" class="form-control"></p> 223 <p>作者 224 <select name="authors" id="" multiple class="form-control"> 225 {% for author_obj in author_queryset %} 226 {# 如果循环的作者对象在书籍对象对应的作者列表中,则显示默认选中状态#} 227 {% if author_obj in book_obj.authors.all %} 228 <option value="{{ author_obj.pk }}" selected>{{ author_obj.name }}</option> 229 {% else %} 230 <option value="{{ author_obj.pk }}">{{ author_obj.name }}</option> 231 {% endif %} 232 233 {% endfor %} 234 </select> 235 </p> 236 <p><input type="submit" value="提交" class="btn btn-primary btn-block"></p> 237 </form> 238 {% endblock %} 239 240 241 ************************************************************ 242 # book_add.html 243 244 {% extends 'home.html' %} 245 246 {% block content %} 247 <h1>添加书籍</h1> 248 <form action="" method="post"> 249 <p>书名<input type="text" name="title" class="form-control"></p> 250 <p>价格<input type="text" name="price" class="form-control"></p> 251 <p>出版社 252 <select name="publish" id="" class="form-control"> 253 {% for publish_obj in publish_queryset %} 254 <option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option> 255 {% endfor %} 256 257 </select> 258 </p> 259 <p>出版时间<input type="date" name="publish_time" class="form-control"></p> 260 <p>作者 261 <select name="authors" id="" multiple class="form-control"> 262 {% for author_obj in author_queryset %} 263 <option value="{{ author_obj.pk }}">{{ author_obj.name }}</option> 264 {% endfor %} 265 </select> 266 </p> 267 <p><input type="submit" value="提交" class="btn btn-primary btn-block"></p> 268 </form> 269 {% endblock %} 270
choices参数(数据库字段设计常见)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 """ 2 用户表 3 性别 4 学历 5 工作经验 6 是否结婚 7 是否生子 8 客户来源 9 ... 10 针对某个可以列举完全的可能性字段,我们应该如何存储 11 12 只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数 13 """ 14 class User(models.Model): 15 username = models.CharField(max_length=32) 16 age = models.IntegerField() # IntegerFiled对应gender_choices中的数字 17 # 性别 18 gender_choices = ( 19 (1,'男'), 20 (2,'女'), 21 (3,'其他'), 22 ) 23 gender = models.IntegerField(choices=gender_choices) 24 25 score_choices = ( 26 ('A','优秀'), 27 ('B','良好'), 28 ('C','及格'), 29 ('D','不合格'), 30 ) 31 # 保证字段类型跟列举出来的元祖第一个数据类型一致即可 32 score = models.CharField(choices=score_choices,null=True) 33 # CharField对应score_choices中的'A'、'B'...字符串字母 34 """ 35 该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内 36 那么可以非常轻松的获取到数字对应的真正的内容 37 38 1.gender字段存的数字不在上述元祖列举的范围内容 39 2.如果在 如何获取对应的中文信息 40 """ 41 42 # test.py 测试文件 43 from app01 import models 44 # models.User.objects.create(username='jason',age=18,gender=1) 45 # models.User.objects.create(username='egon',age=85,gender=2) 46 # models.User.objects.create(username='tank',age=40,gender=3) 47 # 存的时候 没有列举出来的数字也能存(范围还是按照字段类型决定) 48 # models.User.objects.create(username='tony',age=45,gender=4) 49 50 # 取 51 # user_obj = models.User.objects.filter(pk=1).first() 52 # print(user_obj.gender) 53 # 只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display() 54 # print(user_obj.get_gender_display()) 55 56 user_obj = models.User.objects.filter(pk=4).first() 57 # 如果没有对应关系 那么字段是什么还是展示什么 58 print(user_obj.get_gender_display()) # 4 59 60 61 # 实际项目案例 62 # CRM相关内部表 63 class School(models.Model): 64 """ 65 校区表 66 如: 67 北京沙河校区 68 上海校区 69 70 """ 71 title = models.CharField(verbose_name='校区名称', max_length=32) 72 73 def __str__(self): 74 return self.title 75 76 class Course(models.Model): 77 """ 78 课程表 79 如: 80 Linux基础 81 Linux架构师 82 Python自动化开发精英班 83 Python自动化开发架构师班 84 Python基础班 85 go基础班 86 """ 87 name = models.CharField(verbose_name='课程名称', max_length=32) 88 89 def __str__(self): 90 return self.name 91 92 class Department(models.Model): 93 """ 94 部门表 95 市场部 1000 96 销售 1001 97 98 """ 99 title = models.CharField(verbose_name='部门名称', max_length=16) 100 code = models.IntegerField(verbose_name='部门编号', unique=True, null=False) 101 102 def __str__(self): 103 return self.title 104 105 class UserInfo(models.Model): 106 """ 107 员工表 108 """ 109 110 name = models.CharField(verbose_name='员工姓名', max_length=16) 111 email = models.EmailField(verbose_name='邮箱', max_length=64) 112 depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code") 113 user=models.OneToOneField("User",default=1) 114 def __str__(self): 115 return self.name 116 117 class ClassList(models.Model): 118 """ 119 班级表 120 如: 121 Python全栈 面授班 5期 10000 2017-11-11 2018-5-11 122 """ 123 school = models.ForeignKey(verbose_name='校区', to='School') 124 course = models.ForeignKey(verbose_name='课程名称', to='Course') 125 semester = models.IntegerField(verbose_name="班级(期)") 126 127 128 price = models.IntegerField(verbose_name="学费") 129 start_date = models.DateField(verbose_name="开班日期") 130 graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True) 131 memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, ) 132 133 teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',limit_choices_to={'depart':1002}) 134 tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo',related_name="class_list",limit_choices_to={'depart':1006}) 135 136 137 def __str__(self): 138 return "{0}({1}期)".format(self.course.name, self.semester) 139 140 141 class Customer(models.Model): 142 """ 143 客户表 144 """ 145 qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一') 146 147 name = models.CharField(verbose_name='学生姓名', max_length=16) 148 gender_choices = ((1, '男'), (2, '女')) 149 gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices) 150 151 education_choices = ( 152 (1, '重点大学'), 153 (2, '普通本科'), 154 (3, '独立院校'), 155 (4, '民办本科'), 156 (5, '大专'), 157 (6, '民办专科'), 158 (7, '高中'), 159 (8, '其他') 160 ) 161 education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, ) 162 graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True) 163 major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True) 164 165 experience_choices = [ 166 (1, '在校生'), 167 (2, '应届毕业'), 168 (3, '半年以内'), 169 (4, '半年至一年'), 170 (5, '一年至三年'), 171 (6, '三年至五年'), 172 (7, '五年以上'), 173 ] 174 experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices) 175 work_status_choices = [ 176 (1, '在职'), 177 (2, '无业') 178 ] 179 work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True, 180 null=True) 181 company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True) 182 salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True) 183 184 source_choices = [ 185 (1, "qq群"), 186 (2, "内部转介绍"), 187 (3, "官方网站"), 188 (4, "百度推广"), 189 (5, "360推广"), 190 (6, "搜狗推广"), 191 (7, "腾讯课堂"), 192 (8, "广点通"), 193 (9, "高校宣讲"), 194 (10, "渠道代理"), 195 (11, "51cto"), 196 (12, "智汇推"), 197 (13, "网盟"), 198 (14, "DSP"), 199 (15, "SEO"), 200 (16, "其它"), 201 ] 202 source = models.SmallIntegerField('客户来源', choices=source_choices, default=1) 203 referral_from = models.ForeignKey( 204 'self', 205 blank=True, 206 null=True, 207 verbose_name="转介绍自学员", 208 help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名", 209 related_name="internal_referral" 210 ) 211 course = models.ManyToManyField(verbose_name="咨询课程", to="Course") 212 213 status_choices = [ 214 (1, "已报名"), 215 (2, "未报名") 216 ] 217 status = models.IntegerField( 218 verbose_name="状态", 219 choices=status_choices, 220 default=2, 221 help_text=u"选择客户此时的状态" 222 ) 223 224 consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter',limit_choices_to={'depart':1001}) 225 226 date = models.DateField(verbose_name="咨询日期", auto_now_add=True) 227 recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True) 228 last_consult_date = models.DateField(verbose_name="最后跟进日期", ) 229 230 def __str__(self): 231 return self.name 232 233 class ConsultRecord(models.Model): 234 """ 235 客户跟进记录 236 """ 237 customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer') 238 consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo',limit_choices_to={'depart':1001}) 239 date = models.DateField(verbose_name="跟进日期", auto_now_add=True) 240 note = models.TextField(verbose_name="跟进内容...") 241 242 def __str__(self): 243 return self.customer.name + ":" + self.consultant.name 244 245 class Student(models.Model): 246 """ 247 学生表(已报名) 248 """ 249 customer = models.OneToOneField(verbose_name='客户信息', to='Customer') 250 class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True) 251 252 emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人') 253 company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True) 254 location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True) 255 position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True) 256 salary = models.IntegerField(verbose_name='薪资', blank=True, null=True) 257 welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True) 258 date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True) 259 memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True) 260 261 def __str__(self): 262 return self.customer.name 263 264 class ClassStudyRecord(models.Model): 265 """ 266 上课记录表 (班级记录) 267 """ 268 class_obj = models.ForeignKey(verbose_name="班级", to="ClassList") 269 day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字") 270 teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo',limit_choices_to={'depart':1002}) 271 date = models.DateField(verbose_name="上课日期", auto_now_add=True) 272 273 course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True) 274 course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True) 275 has_homework = models.BooleanField(default=True, verbose_name="本节有作业") 276 homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True) 277 homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True) 278 exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True) 279 280 def __str__(self): 281 return "{0} day{1}".format(self.class_obj, self.day_num) 282 283 class StudentStudyRecord(models.Model): 284 ''' 285 学生学习记录 286 ''' 287 classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord") 288 student = models.ForeignKey(verbose_name="学员", to='Student') 289 290 291 292 293 294 295 296 record_choices = (('checked', "已签到"), 297 ('vacate', "请假"), 298 ('late', "迟到"), 299 ('noshow', "缺勤"), 300 ('leave_early', "早退"), 301 ) 302 record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64) 303 score_choices = ((100, 'A+'), 304 (90, 'A'), 305 (85, 'B+'), 306 (80, 'B'), 307 (70, 'B-'), 308 (60, 'C+'), 309 (50, 'C'), 310 (40, 'C-'), 311 (0, ' D'), 312 (-1, 'N/A'), 313 (-100, 'COPY'), 314 (-1000, 'FAIL'), 315 ) 316 score = models.IntegerField("本节成绩", choices=score_choices, default=-1) 317 homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True) 318 note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True) 319 320 homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None) 321 stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True) 322 date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True) 323 324 def __str__(self): 325 return "{0}-{1}".format(self.classstudyrecord, self.student) 326 327 """ 328 chocies参数使用场景是非常广泛的 329 """
MTV与MVC模型
1 # MTV:Django号称是MTV模型 2 M:models 3 T:templates 4 V:views 5 # MVC:其实django本质也是MVC 6 M:models 7 V:views 8 C:controller 9 10 # vue框架:MVVM模型
django多对多关联表的三种创建方式
1 # 全自动:利用orm自动帮我们创建第三张关系表 2 class Book(models.Model): 3 name = models.CharField(max_length=32) 4 authors = models.ManyToManyField(to='Author') 5 class Author(models.Model): 6 name = models.CharField(max_length=32) 7 """ 8 优点:代码不需要你写 非常的方便 还支持orm提供操作第三张关系表的方法... 9 不足之处:第三张关系表的扩展性极差(没有办法额外添加字段...) 10 """ 11 # 纯手动 12 class Book(models.Model): 13 name = models.CharField(max_length=32) 14 15 class Author(models.Model): 16 name = models.CharField(max_length=32) 17 18 class Book2Author(models.Model): 19 book_id = models.ForeignKey(to='Book') 20 author_id = models.ForeignKey(to='Author') 21 ''' 22 优点:第三张表完全取决于你自己进行额外的扩展 23 不足之处:需要写的代码较多,不能够再使用orm提供的简单的方法 24 不建议你用该方式 25 ''' 26 27 # 半自动 28 class Book(models.Model): 29 name = models.CharField(max_length=32) 30 authors = models.ManyToManyField(to='Author', 31 through='Book2Author', 32 through_fields=('book','author') 33 ) 34 class Author(models.Model): 35 name = models.CharField(max_length=32) 36 # books = models.ManyToManyField(to='Book', 37 # through='Book2Author', 38 # through_fields=('author','book') 39 # ) 40 class Book2Author(models.Model): 41 book = models.ForeignKey(to='Book') 42 author = models.ForeignKey(to='Author') 43 44 """ 45 through_fields用元组,字段先后顺序 46 判断的本质: 47 通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面 48 你也可以简化判断 49 当前表是谁 就把对应的关联字段放前面 50 51 52 半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法 53 """ 54 55 # 总结:需要掌握的是全自动和半自动 为了扩展性更高 一般我们都会采用半自动(写代码要给自己留一条后路)
七、Ajax简介
ajax小例子
1 """ 2 页面上有三个input框 3 在前两个框中输入数字 点击按钮 朝后端发送ajax请求 4 后端计算出结果 再返回给前端动态展示的到第三个input框中 5 (整个过程页面不准有刷新,也不能在前端计算) 6 """ 7 # 后端 8 def ab_ajax(request): 9 if request.method == 'POST': 10 inp1 = request.POST.get('i1') 11 inp2 = request.POST.get('i2') 12 inp3 = int(inp1) + int(inp2) 13 d = {'aa':1,'bb':inp3} 14 return HttpResponse(json.dumps(d)) 15 return render(request,'test.html') 16 17 # 前端 18 $('#btn').click(function () { 19 // 朝后端发送ajax请求 20 $.ajax({ 21 // 1.指定朝哪个后端发送ajax请求 22 url:'', // 不写就是朝当前地址提交 23 // 2.请求方式 24 type:'post', // 不指定默认就是get 都是小写 25 // 3.数据 26 // data:{'username':'jason','password':123}, 27 data:{'i1':$('#d1').val(),'i2':$('#d2').val()}, 28 dataType:'JSON', 29 // 4.回调函数:当后端给你返回结果的时候会自动触发 args接受后端的返回结果 30 success:function (args) { 31 {#alert(args) // 通过DOM操作动态渲染到第三个input里面#} 32 ('#inp3').val(args) 33 console.log(typeof args) 34 35 } 36 }) 37 }) 38 39 40 """ 41 写ajax的时候 你可以直接将dataType参数加上 以防万一 或者后端就用JsonResonse 42 针对后端如果是用HttpResponse返回的数据 回调函数不会自动帮你反序列化 可以将dataType参数加上 43 如果后端直接用的是JsonResponse返回的数据 回调函数会自动帮你反序列化 44 45 HttpResponse解决方式 46 1.自己在前端利用JSON.parse() 47 2.或者在ajax里面配置一个 dataType:'JSON' 参数 48 """ 49
前后端传输数据的编码格式(contentType)
1 # 我们主要研究post请求数据的编码格式 2 """ 3 get请求数据就是直接放在url后面的 4 url?username=jason&password=123 5 """ 6 7 # 可以朝后端发送post请求的两种方式 8 """ 9 1.form表单 10 2.ajax请求 11 """ 12 13 14 """ 15 前后端传输数据的三种编码格式: 16 urlencoded 17 18 formdata 19 20 application/json 21 """ 22 # 研究form表单:只能发urlencoded和formdata编码格式的数据,不能发application/json格式数据 23 默认的数据编码格式是urlencoded 24 数据格式:username=jason&password=123 25 django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中 26 username=jason&password=123 >>> request.POST 27 28 如果你把编码格式改成formdata,form表单就既可以发送普通键值对的值,也可以发送文件,那么针对普通的键值对还是解析到request.POST中而将文件解析到request.FILES中 29 30 'form表单是没有办法发送json格式数据的' 31 32 33 # 研究ajax:三种编码格式数据都能发,urlencoded、formdata、application/json 34 默认的编码格式也是urlencoded 35 数据格式:username=jason&age=20 36 django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中 37 username=jason&age=20 >>> request.POST 38
ajax发送json格式数据
1 """ 2 前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的 3 不要骗人家!!! 4 5 {"username":"jason","age":25} 6 在request.POST里面肯定找不到 7 8 django针对json格式的数据 不会做任何的处理 9 10 request对象方法补充 11 request.is_ajax() 12 判断当前请求是否是ajax请求 返回布尔值 13 14 """ 15 16 <script> 17 $('#d1').click(function () { 18 $.ajax({ 19 url:'', 20 type:'post', 21 data:JSON.stringify({'username':'jason','age':25}), 22 contentType:'application/json', // 指定编码格式,编码格式一定要跟上面的data数据格式一致 23 success:function () { 24 25 } 26 }) 27 }) 28 </script> 29 30 # 后端 31 json_bytes = request.body 32 json_str = json_bytes.decode('utf-8') 33 json_dict = json.loads(json_str) 34 35 # json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化 36 json_dict = json.loads(json_bytes) 37 38 """ 39 ajax发送json格式数据需要注意点 40 1.contentType参数指定成:application/json 41 2.数据是真正的json格式数据 42 3.django后端不会帮你处理json格式数据需要你自己去request.body获取并处理 43 """
ajax发送文件
1 """ 2 要发送文件 就必须指定编码格式为FormData的数据 3 ajax发送文件 即发送的是FormData编码格式的数据 需要借助于js内置对象FormData 所以要生成一个FormData对象 4 """ 5 # 前端代码: 6 <script> 7 // 点击按钮朝后端发送普通键值对和文件数据 8 $('#d4').on('click',function () { 9 // 1 需要先利用FormData内置对象 10 let formDateObj = new FormData(); 11 // 2 添加普通的键值对 12 formDateObj.append('username',$('#d1').val()); 13 formDateObj.append('password',$('#d2').val()); 14 // 3 添加文件对象 15 formDateObj.append('myfile',$('#d3')[0].files[0]) 16 // 4 将对象基于ajax发送给后端 17 $.ajax({ 18 url:'', 19 type:'post', 20 data:formDateObj, // 直接将对象放在data后面即可 21 // ajax发送文件必须要指定的两个参数 22 contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象 23 processData:false, // 告诉你的浏览器不要对你的数据进行任何处理 24 25 success:function (args) { 26 } 27 }) 28 29 30 }) 31 </script> 32 33 # 后端代码: 34 def ab_file(request): 35 if request.is_ajax(): 36 if request.method == 'POST': 37 print(request.POST) 38 print(request.FILES) 39 return render(request,'ab_file.html') 40 41 """ 42 总结: 43 1.需要利用内置对象FormData 44 // 1.1 添加普通的键值对 45 formDateObj.append('username',$('#d1').val()); 46 formDateObj.append('password',$('#d2').val()); 47 // 1.2 添加文件对象 48 formDateObj.append('myfile',$('#d3')[0].files[0]) 49 2.需要指定两个关键性的参数 50 contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象 51 processData:false, // 告诉你的浏览器不要对你的数据进行任何处理 52 3.django后端能够直接识别到formdata对象并且能够将内部的普通键值自动解析并封装到request.POST中 将文件数据自动解析并封装到request.FILES中 53 4.还可以用request.is_ajax()来判断是否是ajax请求 54 """
django自带的序列化组件(drf做铺垫)
1 """ 2 正常前后端分离的情况下,如果我们只负责后端代码,前端代码并不是由我们自己书写,就没办法使用Django中的templates模板语法,因此,我们需要自己在后端,将数据封装好,通常都是封装成列表套字典的格式,如果数据量太大的情况下,我们自己封装通常太繁琐,这就需要用到Django自带的序列化组件帮助我们封装数据 3 """ 4 # 需求:在前端给我获取到后端用户表里面所有的数据 并且要是列表套字典 5 import json 6 from django.http import JsonResponse 7 # 方式一:我们自己定义字典,然后将数据以列表套字典的格式发给前端,不推荐,因为数据多时不可能手动封装 8 def ab_ser(request): 9 user_list = [] 10 user_queryset = models.User.objects.all() 11 for user_obj in user_queryset: 12 tmp = { 13 'pk':user_obj.pk, 14 'username':user_obj.username, 15 'age':user_obj.age, 16 'gender':user_obj.get_gender_display() 17 } 18 user_list.append(tmp) 19 return JsonResponse(user_list,safe=False) 20 return render(request,'ab_ser.html',locals()) 21 'Jsonresponse默认的safe参数是True 如果你传入的data数据类型不是字典类型,那么它就会抛出异常' 22 23 24 # 方式二:通过django自带的序列化组件帮我们封装,推荐使用 25 from django.core import serializers 26 27 def ab_ser(request): 28 user_queryset = models.User.objects.all() 29 res = serializers.serialize('json',user_queryset) # 序列化 30 """会自动帮你将数据变成json格式的字符串 并且内部非常的全面""" 31 return HttpResponse(res) 32 33 34 """ 35 [ 36 {"pk": 1, "username": "jason", "age": 25, "gender": "male"}, 37 {"pk": 2, "username": "egon", "age": 31, "gender": "female"}, 38 {"pk": 3, "username": "kevin", "age": 32, "gender": "others"}, 39 {"pk": 4, "username": "tank", "age": 40, "gender": 4} 40 ] 41 前后端分离的项目 42 作为后端开发的你只需要写代码将数据处理好 43 能够序列化返回给前端即可 44 再写一个接口文档 告诉前端每个字段代表的意思即可 45 46 47 [ 48 { "model": "app01.user", 49 "pk": 1, 50 "fields": {"username": "jason", "age": 25, "gender": 1}}, 51 52 { "model": "app01.user", 53 "pk": 2, 54 "fields": {"username": "egon", "age": 31, "gender": 2}}, 55 56 { "model": "app01.user", 57 "pk": 3, 58 "fields": {"username": "kevin", "age": 32, "gender": 3}}, 59 60 { "model": "app01.user", 61 "pk": 4, 62 "fields": {"username": "tank", "age": 40, "gender": 4}} 63 ] 64 写接口就是利用序列化组件渲染数据然后写一个接口文档 该交代交代一下就完事 65 """ 66
ajax结合sweetalert
1 """ 2 自己要学会如何拷贝 3 学会基于别人的基础之上做修改 4 研究各个参数表示的意思 然后照葫芦画瓢 5 """ 6 # views.py 7 def userlist(request): 8 user_queryset = models.User.objects.all() 9 return render(request,'userlist.html',locals()) 10 11 12 def del_user(request): 13 if request.is_ajax(): 14 if request.method == 'POST': 15 back_dic = {'code':1000, 'msg':''} # 给前端页面返回一个字典,提示是否删除 16 time.sleep(3) # 用睡眠模拟删除数据库的删除等待过程 17 del_id = request.POST.get('del_id') 18 models.User.objects.filter(pk=del_id).delete() 19 back_dic['msg'] = '已经删除了,快跑!!!' # 如果删除成功,就给msg赋值 20 return JsonResponse(back_dic) # 将字典返回给前端的回调函数 21 ************************************************************ 22 23 # userlist.html 24 <script> 25 $('.del').on('click',function () { 26 // 先将当前标签对象存储起来 27 let currentBtn = $(this); 28 // 二次确认弹框 29 swal({ 30 title: "你确定要删吗?", 31 text: "你可要考虑清除哦,可能需要拎包跑路哦!", 32 type: "warning", 33 showCancelButton: true, 34 confirmButtonClass: "btn-danger", 35 confirmButtonText: "是的,老子就要删!", 36 cancelButtonText: "算了,算了!", 37 closeOnConfirm: false, 38 closeOnCancel: false, 39 showLoaderOnConfirm: true 40 }, 41 function(isConfirm) { 42 if (isConfirm) { 43 // 朝后端发送ajax请求删除数据之后 再弹下面的提示框 44 $.ajax({ 45 {#url:'/delete/user/' + currentBtn.attr('delete_id'), // 1 传递主键值方式1#} 46 url:'/delete/user/', // 2 放在请求体里面 47 type:'post', 48 data:{'delete_id':currentBtn.attr('delete_id')}, 49 success:function (args) { // args = {'code':'','msg':''} 50 // 通过回调函数接收到的参数,判断响应状态码 然后做不同的处理 51 if(args.code === 1000){ 52 swal("删了!", args.msg, "success"); 53 // 1.lowb版本 直接刷新当前页面 54 {#window.location.reload()#} 55 // 2.利用DOM操作 动态刷新 56 currentBtn.parent().parent().remove() 57 }else{ 58 swal('完了','出现了位置的错误','info') 59 } 60 } 61 62 }) 63 64 } else { 65 swal("怂逼", "不要说我认识你", "error"); 66 } 67 }); 68 }) 69 70 </script> 71
批量插入
1 # 第一种,普通插入,不推荐 2 def ab_pl(request): 3 # 先给Book插入一万条数据 4 for i in range(10000): 5 models.Book.objects.create(title='第%s本书'%i) 6 # 再将所有的数据查询并展示到前端页面 7 book_queryset = models.Book.objects.all() 8 return render(request,'ab_pl.html',locals()) 9 10 # 第二种方式,批量插入,推荐使用 11 def ab_pl(request): 12 # 批量插入 13 book_list = [] 14 for i in range(100000): 15 book_obj = models.Book(title='第%s本书'%i) 16 book_list.append(book_obj) 17 models.Book.objects.bulk_create(book_list) 18 """ 19 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间 20 """ 21 return render(request,'ab_pl.html',locals())
分页器
1 """ 2 总数据100 每页展示10 需要10 3 总数据101 每页展示10 需要11 4 总数据99 每页展示10 需要10 5 6 如何通过代码动态的计算出到底需要多少页? 7 8 9 在制作页码个数的时候 一般情况下都是奇数个 符合中国人对称美的标准 10 """ 11 # 分页 12 book_list = models.Book.objects.all() 13 14 # 想访问哪一页 15 current_page = request.GET.get('page',1) # 如果获取不到当前页码 就展示第一页 16 # 数据类型转换 17 try: 18 current_page = int(current_page) 19 except Exception: 20 current_page = 1 21 # 每页展示多少条 22 per_page_num = 10 23 # 起始位置 24 start_page = (current_page - 1) * per_page_num 25 # 终止位置 26 end_page = current_page * per_page_num 27 28 # 计算出到底需要多少页 29 all_count = book_list.count() 30 31 page_count, more = divmod(all_count, per_page_num) 32 if more: 33 page_count += 1 34 35 page_html = '' 36 xxx = current_page 37 if current_page < 6: 38 current_page = 6 39 for i in range(current_page-5,current_page+6): 40 if xxx == i: 41 page_html += '<li class="active"><a href="?page=%s">%s</a></li>'%(i,i) 42 else: 43 page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i) 44 45 46 47 book_queryset = book_list[start_page:end_page] 48 49 """ 50 django中有自带的分页器模块 但是书写起来很麻烦并且功能太简单 51 所以我们自己想法和设法的写自定义分页器 52 53 上述推导代码你无需掌握 只需要知道内部逻辑即可 54 55 我们基于上述的思路 已经封装好了我们自己的自定义分页器 56 之后需要使用直接拷贝即可 57 """
自定义分页器的拷贝及使用
1 """ 2 当我们需要使用到非django内置的第三方功能或者组件代码的时候 3 我们一般情况下会创建一个名为utils文件夹 在该文件夹内对模块进行功能性划分 4 utils可以在每个应用下创建 具体结合实际情况 5 6 我们到了后期封装代码的时候 不再局限于函数 7 还是尽量朝面向对象去封装 8 9 我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap 10 bootstrap 版本 v3 11 jQuery 版本 v3 12 """ 13 # 后端 14 book_queryset = models.Book.objects.all() 15 current_page = request.GET.get('page',1) 16 all_count = book_queryset.count() 17 # 1 传值生成对象 18 page_obj = Pagination(current_page=current_page,all_count=all_count) 19 # 2 直接对总数据进行切片操作 20 page_queryset = book_queryset[page_obj.start:page_obj.end] 21 # 3 将page_queryset传递到页面 替换之前的book_queryset 22 render(request,'utils.html',locals()) 23 24 25 # 前端 26 {% for book_obj in page_queryset %} 27 <p>{{ book_obj.title }}</p> 28 {% endfor %} 29 {#利用自定义分页器直接显示分页器样式#} 30 {{ page_obj.page_html|safe }} 31 32 """ 33 你们只需要掌握如何拷贝使用 以及大致的推导思路即可 34 """ 35
八、Forms组件
前戏
1 """ 2 写一个注册功能 3 获取用户名和密码 利用form表单提交数据 4 在后端判断用户名和密码是否符合一定的条件 5 用户名中不能含有金xx 6 密码不能少于三位 7 8 如何符合条件需要你将提示信息展示到前端页面 9 """ 10 11 # 后端 12 def ab_form(request): 13 back_dic = {'username':'','password':''} 14 if request.method == 'POST': 15 username = request.POST.get('username') 16 password = request.POST.get('password') 17 if '金xx' in username: 18 back_dic['username'] = '不符合社会主义核心价值观' 19 if len(password) < 3: 20 back_dic['password'] = '不能太短 不好!' 21 """ 22 无论是post请求还是get请求 23 页面都能够获取到字典 只不过get请求来的时候 字典值都是空的 24 而post请求来之后 字典可能有值 25 """ 26 return render(request,'ab_form.html',locals()) 27 28 29 # 前端 30 <form action="" method="post"> 31 <p>username: 32 <input type="text" name="username"> 33 <span style="color: red">{{ back_dic.username }}</span> 34 </p> 35 <p>password: 36 <input type="text" name="password"> 37 <span style="color: red">{{ back_dic.password }}</span> 38 </p> 39 <input type="submit" class="btn btn-info"> 40 </form> 41 42 43 """ 44 1.手动书写前端获取用户数据的html代码 渲染html代码 45 2.后端对用户数据进行校验 校验数据 46 3.对不符合要求的数据进行前端提示 展示提示信息 47 48 forms组件 49 能够完成的事情 50 1.渲染html代码 51 2.校验数据 52 3.展示提示信息 53 54 为什么数据校验非要去后端 不能在前端利用js直接完成呢? 55 数据校验前端可有可无 56 但是后端必须要有!!! 57 58 因为前端的校验是弱不禁风的 你可以直接修改 59 或者利用爬虫程序绕过前端页面直接朝后端提交数据 60 61 购物网站 62 选取了货物之后 会计算一个价格发送给后端 如果后端不做价格的校验 63 64 实际是获取到用户选择的所有商品的主键值 65 然后在后端查询出所有商品的价格 再次计算一遍 66 如果跟前端一致 那么完成支付如果不一致直接拒绝 67 """
1 from django import forms 2 3 4 class MyForm(forms.Form): 5 # username字符串类型最小3位最大8位 6 username = forms.CharField(min_length=3,max_length=8) 7 # password字符串类型最小3位最大8位 8 password = forms.CharField(min_length=3,max_length=8) 9 # email字段必须符合邮箱格式 xxx@xx.com 10 email = forms.EmailField() 11
forms组件之校验数据
1 """ 2 1.测试环境的准备 可以自己拷贝代码准备 3 2.其实在pycharm里面已经帮你准备一个测试环境 4 python console 5 """ 6 7 # python console中: 8 from app01 import views 9 # 1 将带校验的数据组织成字典的形式传入即可 10 form_obj = views.MyForm({'username':'jason','password':'123','email':'123'}) 11 # 2 判断数据是否合法 注意该方法只有在所有的数据全部合法的情况下才会返回True 12 form_obj.is_valid() 13 False 14 # 3 查看所有校验通过的数据 15 form_obj.cleaned_data 16 {'username': 'jason', 'password': '123'} 17 # 4 查看所有不符合校验规则以及不符合的原因 18 form_obj.errors 19 { 20 'email': ['Enter a valid email address.'] 21 } 22 # 5 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略 23 form_obj = views.MyForm({'username':'jason','password':'123','email':'123@qq.com','hobby':'study'}) 24 form_obj.is_valid() 25 True 26 # 6 校验数据 默认情况下 类里面所有的字段都必须传值 27 form_obj = views.MyForm({'username':'jason','password':'123'}) 28 form_obj.is_valid() 29 False 30 """ 31 也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传 32 """
forms组件之渲染标签
1 """ 2 forms组件只会自动帮你渲染获取用户输入的标签(input select radio checkbox) 3 不能帮你渲染提交按钮 4 forms组件渲染出来的input标签的id都是 id_字段名 的格式 5 6 """ 7 def index(request): 8 # 1 先产生一个空对象 9 form_obj = MyForm() 10 # 2 直接将该空对象传递给html页面 11 return render(request,'index.html',locals()) 12 13 # 前端利用空对象做操作 14 <p>第一种渲染方式:代码书写极少,封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用</p> 15 {{ form_obj.as_p }} 16 {{ form_obj.as_ul }} 17 {{ form_obj.as_table }} 18 19 <p>第二种渲染方式:可扩展性很强 但是需要书写的代码太多 一般情况下不用</p> 20 <p>{{ form_obj.username.label }}:{{ form_obj.username }}</p> 21 <p>{{ form_obj.password.label }}:{{ form_obj.password }}</p> 22 <p>{{ form_obj.email.label }}:{{ form_obj.email }}</p> 23 24 <p>第三种渲染方式(推荐使用):代码书写简单 并且扩展性也高</p> 25 {% for form in form_obj %} 26 <p>{{ form.label }}:{{ form }}</p> 27 {% endfor %} 28 # 通过{{form.auto_id}}可以直接拿到对应的input框的id值 29 """ 30 label属性默认展示的是类中定义的字段首字母大写的形式 31 也可以自己修改 直接给字段对象加label属性即可 32 username = forms.CharField(min_length=3,max_length=8,label='用户名') 33 """
forms之展示提示信息
1 """ 2 浏览器会自动帮你校验数据 但是前端的校验弱不禁风 3 如何让浏览器不做校验:<form action="" method="post" novalidate> 4 """ 5 # 后端 6 def index(request): 7 # 1 先产生一个空对象 8 form_obj = MyForm() 9 if request.method == 'POST': 10 # 获取用户数据并且校验 11 """ 12 1.数据获取繁琐 13 2.校验数据需要构造成字典的格式传入才行 14 ps:但是request.POST可以看成就是一个字典 15 """ 16 # 3.校验数据 17 form_obj = MyForm(request.POST) # 将request.POST获取到的值重新赋值给对象 18 # 4.判断数据是否合法 19 if form_obj.is_valid(): 20 # 5.如果合法 操作数据库存储数据 21 return HttpResponse('OK') 22 # 5.不合法 有错误 23 # 2 直接将该空对象传递给html页面 24 return render(request,'index.html',locals()) 25 26 27 # 前端 28 <form action="" method="post" novalidate> 29 {% for form in form_obj %} 30 <p> 31 {{ form.label }}:{{ form }} 32 <span style="color: red">{{ form.errors.0 }}</span> 33 </p> 34 {% endfor %} 35 </form> 36 37 """ 38 1.必备的条件 get请求和post传给html页面对象变量名必须一样 39 2.forms组件当你的数据不合法的情况下 会保存你上次的数据 让你基于之前的结果进行修改 40 更加的人性化 41 """ 42 # 针对错误的提示信息还可以自己自定制 43 class MyForm(forms.Form): 44 # username字符串类型最小3位最大8位 45 username = forms.CharField(min_length=3,max_length=8,label='用户名', 46 error_messages={ 47 'min_length':'用户名最少3位', 48 'max_length':'用户名最大8位', 49 'required':"用户名不能为空" 50 } 51 ) 52 # password字符串类型最小3位最大8位 53 password = forms.CharField(min_length=3,max_length=8,label='密码', 54 error_messages={ 55 'min_length': '密码最少3位', 56 'max_length': '密码最大8位', 57 'required': "密码不能为空" 58 } 59 ) 60 # email字段必须符合邮箱格式 xxx@xx.com 61 email = forms.EmailField(label='邮箱', 62 error_messages={ 63 'invalid':'邮箱格式不正确', 64 'required': "邮箱不能为空" 65 } 66 ) 67
forms组件之钩子函数(HOOK)
1 """ 2 在特定的节点自动触发完成响应操作 3 4 钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则 5 6 在forms组件中有两类钩子 7 1.局部钩子 8 当你需要给单个字段增加校验规则的时候可以使用 9 2.全局钩子 10 当你需要给多个字段增加校验规则的时候可以使用 11 """ 12 # 实际案例 13 14 # 1.校验用户名中不能含有666 只是校验username字段 局部钩子 def clean_xxx(self) 15 16 # 2.校验密码和确认密码是否一致 password confirm两个字段 全局钩子 def cleaned(self) 17 18 # 钩子函数 在类里面书写方法即可 19 from django import forms 20 class MyForm(forms.Form): 21 username = forms.CharField(min_length=3,max_length=8, label='用户名',error_messages={ 22 'min_length':'密码最少3位', 23 'max_length':'密码多多8位', 24 'required':'用户名不能为空', 25 }) 26 password = forms.CharField(min_length=3,max_length=8, label='密码', 27 error_messages={ 28 'min_length':'密码最少3位', 29 'max_length':'密码多多8位', 30 'required':'密码不能为空', 31 }) 32 confirm_password = forms.CharField(min_length=3,max_length=8, label='密码', 33 error_messages={ 34 'min_length':'确认密码最少3位', 35 'max_length':'确认密码多多8位', 36 'required':'确认密码不能为空', 37 }) 38 email = forms.EmailField(label='邮箱',error_messages={ 39 'invalid':'邮箱格式错误', 40 'required':'邮箱地址不能为空', 41 }) 42 # 局部钩子 43 def clean_username(self): 44 # 获取到用户名 45 username = self.cleaned_data.get('username') 46 if '666' in username: 47 # 提示前端展示错误信息 48 self.add_error('username','光喊666是不行滴~') 49 # 将钩子函数钩去出来数据再放回去,局部钩子返回给校验的字段 50 return username 51 52 # 全局钩子 53 def clean(self): 54 password = self.cleaned_data.get('password') 55 confirm_password = self.cleaned_data.get('confirm_password') 56 if not confirm_password == password: 57 self.add_error('confirm_password','两次密码不一致') 58 # 将钩子函数钩出来数据再放回去,全局的返回给cleaned_data 59 return self.cleaned_data 60
forms组件之其他参数及补充知识点
1 label 字段名 2 error_messages 自定义报错信息 3 initial 默认值 4 required 控制字段是否必填 5 """ 6 1.字段没有样式 7 2.针对不同类型的input如何修改 8 text 9 password 10 date 11 radio 12 checkbox 13 ... 14 """ 15 widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'}) 16 # 多个属性值的话 直接空格隔开即可 17 18 # 第一道关卡里面还支持正则校验 19 validators=[ 20 RegexValidator(r'^[0-9]+$', '请输入数字'), 21 RegexValidator(r'^159[0-9]+$', '数字必须以159开头') 22 ]
forms组件之其他类型渲染
1 # radio 2 gender = forms.ChoiceField( 3 choices=((1, "男"), (2, "女"), (3, "保密")), 4 label="性别", 5 initial=3, 6 widget=forms.widgets.RadioSelect() 7 ) 8 # select 9 hobby = forms.ChoiceField( 10 choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), 11 label="爱好", 12 initial=3, 13 widget=forms.widgets.Select() 14 ) 15 # 多选 16 hobby1 = forms.MultipleChoiceField( 17 choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), 18 label="爱好", 19 initial=[1, 3], 20 widget=forms.widgets.SelectMultiple() 21 ) 22 # 单选checkbox 23 keep = forms.ChoiceField( 24 label="是否记住密码", 25 initial="checked", 26 widget=forms.widgets.CheckboxInput() 27 ) 28 # 多选checkbox 29 hobby2 = forms.MultipleChoiceField( 30 choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), 31 label="爱好", 32 initial=[1, 3], 33 widget=forms.widgets.CheckboxSelectMultiple() 34 )
forms组件源码
1 """ 2 切入点: 3 form_obj.is_valid() 4 """ 5 def is_valid(self): 6 """ 7 Returns True if the form has no errors. Otherwise, False. If errors are 8 being ignored, returns False. 9 """ 10 return self.is_bound and not self.errors 11 # 如果is_valid要返回True的话 那么self.is_bound要为True self.errors要为Flase 12 13 14 self.is_bound = data is not None or files is not None # 只要你传值了肯定为True 15 16 17 @property 18 def errors(self): 19 "Returns an ErrorDict for the data provided for the form" 20 if self._errors is None: 21 self.full_clean() 22 return self._errors 23 24 # forms组件所有的功能基本都出自于该方法 25 def full_clean(self): 26 self._clean_fields() # 校验字段 + 局部钩子 27 self._clean_form() # 全局钩子 28 self._post_clean() 29
九、会话
cookie与session简介
1 """ 2 发展史 3 1.网站都没有保存用户功能的需求 所有用户访问返回的结果都是一样的 4 eg:新闻、博客、文章... 5 6 2.出现了一些需要保存用户信息的网站 7 eg:淘宝、支付宝、京东... 8 9 以登陆功能为例:如果不保存用户登陆状态 也就意味着用户每次访问网站都需要重复的输入用户名和密码(你觉得这样的网站你还想用吗?) 10 当用户第一次登陆成功之后 将用户的用户名密码返回给用户浏览器 让用户浏览器保存在本地,之后访问网站的时候浏览器自动将保存在浏览器上的用户名和密码发送给服务端,服务端获取之后自动验证 11 早期这种方式具有非常大的安全隐患 12 13 14 优化: 15 当用户登陆成功之后,服务端产生一个随机字符串(在服务端保存数据,用kv键值对的形式),交由客户端浏览器保存 16 随机字符串1:用户1相关信息 17 随机字符串2:用户2相关信息 18 随机字符串3:用户3相关信息 19 之后访问服务端的时候,都带着该随机字符串,服务端去数据库中比对是否有对应的随机字符串从而获取到对应的用户信息 20 21 22 23 但是如果你拿到了截获到了该随机字符串,那么你就可以冒充当前用户 其实还是有安全隐患的 24 25 26 你要知道在web领域没有绝对的安全也没有绝对的不安全 27 """ 28 cookie 29 服务端保存在客户端浏览器上的信息都可以称之为cookie 30 它的表现形式一般都是k:v键值对(可以有多个) 31 session 32 数据是保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个) 33 34 35 下述内容暂时了解即可 先给我搞明白最简单的cookie与session使用再说话! 36 token 37 session虽然数据是保存在服务端的 但是禁不住数据量大 38 服务端不再保存数据 39 登陆成功之后 将一段用户信息进行加密处理(加密算法只有你公司开发知道) 40 将加密之后的结果拼接在信息后面 整体返回给浏览器保存 41 浏览器下次访问的时候带着该信息 服务端自动切去前面一段信息再次使用自己的加密算法 42 跟浏览器尾部的密文进行比对 43 jwt认证 44 三段信息 45 (后期会讲 结合django一起使用) 46 47 总结: 48 1.cookie就是保存在客户端浏览器上的信息 49 2.session就是保存在服务端上的信息 50 3.session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie) 51
# HTTP协议:无状态,无连接,基于请求响应,基于tcp/ip,应用层协议 # mysql:c/s架构:底层基于socket,自己封装的协议,mysql的客户端:navcate(c++图形化界面,实现了请求和响应协议),pymysql(用python语言实现了请求和响应协议) # redis:c/s架构:底层基于socket,自己封装的协议 # docker:c/s架构,基于http协议,使用restfull规范 # elasticsearch:c/s架构,基于http协议,使用restfull规范 # cookie:是存在于浏览器的键值对,向服务端发送请求,携带它过去(不安全) # session:存在于服务端的键值对(放在哪?内存中,文件,mysql,redis) # 缺陷:如果用户量很大,存储需要耗费服务器资源 # token:就是个字符串(既安全,又存个人信息),加密字符串,会有个人信息 # token现在应用非常广泛,契合了前后端分离 # JWT:json web token
Cookie操作
1 # 虽然cookie是服务端告诉客户端浏览器需要保存内容 2 # 但是客户端浏览器可以选择拒绝保存 如果禁止了 那么 只要是需要记录用户状态的网站登陆功能都无法使用了 3 4 # 视图函数的返回值 5 return HttpResponse() 6 return render() 7 return redirect() 8 9 10 obj1 = HttpResponse() 11 # 操作cookie 12 return obj1 13 14 obj2 = render() 15 # 操作cookie 16 return obj2 17 18 obj3 = redirect() 19 # 操作cookie 20 return obj3 21 # 如果你想要操作cookie,你就不得不利用obj对象 22 23 24 """ 25 设置cookie 26 obj.set_cookie(key,value) 27 获取cookie 28 request.COOKIES.get(key) 29 在设置cookie的时候可以添加一个超时时间 30 obj.set_cookie('username', 'jason666',max_age=3,expires=3) 31 32 max_age 33 expires 34 两者都是设置超时时间的 并且都是以秒为单位 35 需要注意的是 针对IE浏览器需要使用expires 36 主动删除cookie(注销功能) 37 obj.delete_cookie 删除cookie 38 39 40 """ 41 # 我们完成一个真正的登陆功能 42 # 校验用户是否登陆的装饰器 43 """ 44 用户如果在没有登陆的情况下想访问一个需要登陆的页面 45 那么先跳转到登陆页面 当用户输入正确的用户名和密码之后 46 应该跳转到用户之前想要访问的页面去 而不是直接写死 47 """ 48 def login_auth(func): 49 def inner(request,*args,**kwargs): 50 # print(request.path_info) 51 # print(request.get_full_path()) # 能够获取到用户上一次想要访问的url 52 target_url = request.get_full_path() 53 if request.COOKIES.get('username'): 54 return func(request,*args,**kwargs) 55 else: 56 return redirect('/login/?next=%s'%target_url) # 跳转并携带target_url 57 return inner 58 59 def login(request): 60 if request.method == 'POST': 61 username = request.POST.get('username') 62 password = request.POST.get('password') 63 if username == 'jason' and password == '123': 64 65 # 获取用户上一次想要访问的url 66 target_url = request.GET.get('next') # 这个结果可能是None 67 if target_url: 68 obj = redirect(target_url) 69 else: 70 # 保存用户登陆状态 71 obj = redirect('/home/') 72 # 让浏览器记录cookie数据 73 obj.set_cookie('username', 'jason666') 74 """ 75 浏览器不单单会帮你存cookie 76 而且后面每次访问你的时候还会带着cookie过来 77 """ 78 # 跳转到一个需要用户登陆之后才能看的页面 79 return obj 80 return render(request,'login.html') 81 82 83 @login_auth 84 def home(request): 85 # 获取cookie信息 判断你有没有 86 # if request.COOKIES.get('username') == 'jason666': 87 # return HttpResponse("home要登录才能看我啊兄弟") 88 # # 没有登陆应该跳转到登陆页面 89 # return redirect('/login/') 90 return HttpResponse("home要登录才能看我啊兄弟") 91 92 @login_auth 93 def index(request): 94 # if request.COOKIES.get('username'): 95 # return HttpResponse('index要登录才能看我啊兄弟') 96 # return redirect('/login/') 97 return HttpResponse('index要登录才能看我啊兄弟') 98 99 @login_auth 100 def info(request): 101 # if request.COOKIES.get('username'): 102 # return HttpResponse('indo要登录才能看我啊兄弟') 103 # return redirect('/login/') 104 return HttpResponse('info要登录才能看我啊兄弟') 105 106 # 注销是在登录的状态下才能注销,所以也需要用到登录认证装饰器,注销成功后再访问其他页面需要重新登录 107 @login_auth 108 def log_out(request): 109 obj = redirect('/login/') 110 obj.delete_cookie() 111 return obj
session操作
1 """ 2 session数据是保存在服务端的(存?),给客户端返回的是一个随机字符串 3 sessionid:随机字符串 4 5 1.在默认情况下操作session的时候需要django默认的一张django_session表 6 数据库迁移命令 7 django会自己创建很多表 django_session就是其中的一张 8 9 10 django默认session的过期时间是14天 11 但是你也可以人为的修改它 12 13 14 设置session 15 request.session['key'] = value 16 17 获取session 18 request.session.get('key') 19 20 设置过期时间 21 request.session.set_expiry() 22 括号内可以放四种类型的参数 23 1.整数 多少秒 24 2.日期对象 到指定日期就失效 25 3.0 一旦当前浏览器窗口关闭立刻失效 26 4.不写 失效时间就取决于django内部全局session默认的失效时间 27 28 清除session 29 request.session.delete() # 只删服务端的 客户端的不删 30 request.session.flush() # 浏览器和服务端都清空(推荐使用) 31 32 33 session是保存在服务端的 但是session的保存位置可以有多种选择 34 1.MySQL 35 2.文件 36 3.redis 37 4.memcache 38 ... 39 40 41 django_session表中的数据条数是取决于浏览器的 42 同一个计算机上(IP地址)同一个浏览器只会有一条数据生效 43 (当session过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除) 44 45 主要是为了节省服务端数据库资源 46 """ 47 48 request.session['hobby'] = 'girl' 49 """ 50 内部发送了那些事 51 1.django内部会自动帮你生成一个随机字符串 52 2.django内部自动将随机字符串和对应的数据存储到django_session表中 53 2.1先在内存中产生操作数据的缓存 54 2.2在响应结果django中间件的时候才真正的操作数据库 55 3.将产生的随机字符串返回给客户端浏览器保存 56 """ 57 request.session.get('hobby') 58 """ 59 内部发送了那些事 60 1.自动从浏览器请求中获取sessionid对应的随机字符串 61 2.拿着该随机字符串去django_session表中查找对应的数据 62 3. 63 如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中 64 如果比对不上 则request.session.get()返回的是None 65 """ 66 67 68 # 利用session实现登陆验证 69 70 # session登录验证装饰器 71 def login_auth(func): 72 def inner(request,*args,**kwargs): 73 target_url = request.get_full_path() 74 if request.session.get('yesir'): 75 res = func(request,*args,**kwargs) 76 return res 77 else: 78 return redirect('/login/?next={}'.format(target_url)) 79 return inner 80 81 82 def login(request): 83 target_url = request.GET.get('next') 84 if request.method == 'POST': 85 username = request.POST.get('username') 86 password = request.POST.get('password') 87 if username == 'yesir' and password == '123': 88 if target_url: 89 request.session['yesir'] = 'nb' 90 return redirect(target_url) 91 else: 92 request.session['yesir'] = 'nb' 93 return redirect('/home/') 94 return render(request,'login.html') 95 96 @login_auth 97 def home(request): 98 return HttpResponse('home需要登录才能看') 99 100 @login_auth 101 def index(request): 102 return HttpResponse('index需要登录才能看') 103 104 @login_auth 105 def info(request): 106 return HttpResponse('info需要登录才能看') 107 108 @login_auth 109 def logout(request): 110 request.session.flush() 111 return redirect('/login/')
django中session的底层原理
# 在中间件中,请求走的时候,process_response, # 取出request.session的modify属性,判断是否是true, # 如果是true,表示在视图函数中修改过session,数据库同步修改, # 如果是false,就不修改,返回给前端sessionid:随机字符串 # 请求来了,通过sessionid,取出随机字符串--》去数据库中查--》把表的数据转成字典, # 赋值给request.session,后面视图函数中就可以操作它了
django中设置Redis作为缓存
详情参考:Django中Redis的使用
# settings.py # redis配置 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } } }
# views.py #1 使用cache(推荐使用这种方法) from django.core.cache import cache cache.set('name',user) #2 直接使用conn对象(不推荐,但是也可以用) from django_redis import get_redis_connection conn = get_redis_connection('default') print(conn.hgetall('xxx'))
CBV如何添加装饰器
1 from django.views import View 2 from django.utils.decorators import method_decorator 3 """ 4 CBV中django不建议你直接给类的方法加装饰器 5 无论该装饰器能都正常给你 都不建议直接加 6 """ 7 8 # @method_decorator(login_auth,name='get') # 方式2(可以添加多个针对不同的方法加不同的装饰器) 9 # @method_decorator(login_auth,name='post') 10 class MyLogin(View): 11 @method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法 12 def dispatch(self, request, *args, **kwargs): 13 return super().dispatch(request,*args,**kwargs) 14 # @method_decorator(login_auth) # 方式1:指名道姓 15 def get(self,request): 16 return HttpResponse("get请求") 17 18 def post(self,request): 19 return HttpResponse('post请求') 20
十、django中间件
1 * django中间件 2 3 首先django自带七个中间件,每个中间件都有各自对应的功能 4 5 并且django还支持程序员自定义中间件 6 7 你在用django开发项目的项目的时候,只要是涉及到全局相关的功能都可以使用中间件方便的完成 8 9 * 全局用户身份校验 10 * 全局用户权限校验(补充) 11 * 全局访问频率校验 12 * ... 13 14 * 基于django中间件一个重要的变成思想(补充) 15 16 * csrf跨站请求伪造 17 18 * auth模块/settings源码(看时间来定)
1 """ 2 django中间件是django的门户 3 1.请求来的时候需要先经过中间件才能到达真正的django后端 4 2.响应走的时候最后也需要经过中间件才能发送出去 5 django自带七个中间件 6 """ 7 django请求生命周期流程图 8 9 研究django中间件代码规律 10 MIDDLEWARE = [ 11 'django.middleware.security.SecurityMiddleware', 12 'django.contrib.sessions.middleware.SessionMiddleware', 13 'django.middleware.common.CommonMiddleware', 14 'django.middleware.csrf.CsrfViewMiddleware', 15 'django.contrib.auth.middleware.AuthenticationMiddleware', 16 'django.contrib.messages.middleware.MessageMiddleware', 17 'django.middleware.clickjacking.XFrameOptionsMiddleware', 18 ] 19 20 class SessionMiddleware(MiddlewareMixin): 21 def process_request(self, request): 22 session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) 23 request.session = self.SessionStore(session_key) 24 def process_response(self, request, response): 25 return response 26 27 class CsrfViewMiddleware(MiddlewareMixin): 28 def process_request(self, request): 29 csrf_token = self._get_token(request) 30 if csrf_token is not None: 31 # Use same token next time. 32 request.META['CSRF_COOKIE'] = csrf_token 33 def process_view(self, request, callback, callback_args, callback_kwargs): 34 return self._accept(request) 35 36 def process_response(self, request, response): 37 return response 38 39 class AuthenticationMiddleware(MiddlewareMixin): 40 def process_request(self, request): 41 request.user = SimpleLazyObject(lambda: get_user(request)) 42 """ 43 django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法 44 1.必须掌握 45 process_request 46 47 process_response 48 2.了解即可 49 process_view 50 51 process_template_response 52 53 process_exception 54 """
如何自定义中间件
1 """ 2 1.在项目名或者应用名下创建一个任意名称的文件夹 3 2.在该文件夹内创建一个任意名称的py文件 4 3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin) 5 然后在这个类里面就可以自定义五个方法了 6 (这五个方法并不是全部都需要书写,用几个写几个) 7 4.需要将类的路径以字符串的形式注册到配置文件中才能生效 8 MIDDLEWARE = [ 9 'django.middleware.security.SecurityMiddleware', 10 'django.contrib.sessions.middleware.SessionMiddleware', 11 'django.middleware.common.CommonMiddleware', 12 'django.middleware.csrf.CsrfViewMiddleware', 13 'django.contrib.auth.middleware.AuthenticationMiddleware', 14 'django.contrib.messages.middleware.MessageMiddleware', 15 'django.middleware.clickjacking.XFrameOptionsMiddleware', 16 '你自己写的中间件的路径1', 17 '你自己写的中间件的路径2', 18 '你自己写的中间件的路径3', 19 ] 20 21 """ 22 """ 23 1.必须掌握 24 process_request 25 1.http请求来的时候需要经过每一个中间件里面的process_request方法 26 经过的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行 27 2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件 28 3.可以返回None或者Httpresponse对象 29 4.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行 30 而是直接原路返回(校验失败不允许访问...) 31 process_request方法就是用来做全局相关的所有限制功能 32 33 34 process_response 35 1.响应走的时候需要结果每一个中间件里面的process_response方法 36 该方法有两个额外的参数request,response 37 2.该方法必须返回一个HttpResponse对象 38 1.默认返回的就是形参response 39 2.你也可以自己返回自己的 40 3.顺序是按照配置文件中注册了的中间件从下往上依次经过 41 如果你没有定义的话 直接跳过执行下一个 42 43 研究如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况 44 是其他情况 45 就是会直接走同级别的process_reponse 然后依次向上返回 46 47 flask框架也有一个中间件但是它的规律 48 只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法 49 50 51 2.了解即可 52 process_view 53 路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该放法 54 顺序是按照配置文件中注册的中间件从上往下的顺序依次执行 55 56 process_template_response 57 返回的HttpResponse对象有render属性的时候才会触发 58 顺序是按照配置文件中注册了的中间件从下往上依次经过 59 60 process_exception 61 当视图函数中出现异常的情况下触发 62 顺序是按照配置文件中注册了的中间件从下往上依次经过 63 """ 64
csrf跨站请求伪造
1 """ 2 钓鱼网站 3 我搭建一个跟正规网站一模一样的界面(中国银行) 4 用户不小心进入到了我们的网站,用户给某个人打钱 5 打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了 6 但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户 7 8 大学英语四六级 9 考之前需要学生自己网站登陆缴费 10 11 内部本质 12 我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框 13 然后我们在内部隐藏一个已经写好name和value的input框 14 15 如何规避上述问题 16 csrf跨站请求伪造校验 17 网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识 18 当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行 19 """
如何符合校验
1 # form表单如何符合校验 2 <form action="" method="post"> 3 {% csrf_token %} 4 <p>username:<input type="text" name="username"></p> 5 <p>target_user:<input type="text" name="target_user"></p> 6 <p>money:<input type="text" name="money"></p> 7 <input type="submit"> 8 </form> 9 10 # ajax如何符合校验 11 // 第一种 利用标签查找获取页面上的随机字符串 12 data:{"username":'jason','csrfmiddlewaretoken':$("[name='csrfmiddlewaretoken']").val()}, 13 // 第二种 利用模版语法提供的快捷书写 14 data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}, 15 // 第三种 通用方式直接拷贝js代码,保存成js文件,并引用到自己的html页面上即可 代码详见下面的js代码模板, 16 data:{"username":'jason'} 17
ajax设置csrf_token的js代码模板:
1 function getCookie(name) { 2 var cookieValue = null; 3 if (document.cookie && document.cookie !== '') { 4 var cookies = document.cookie.split(';'); 5 for (var i = 0; i < cookies.length; i++) { 6 var cookie = jQuery.trim(cookies[i]); 7 // Does this cookie string begin with the name we want? 8 if (cookie.substring(0, name.length + 1) === (name + '=')) { 9 cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 10 break; 11 } 12 } 13 } 14 return cookieValue; 15 } 16 var csrftoken = getCookie('csrftoken'); 17 18 19 function csrfSafeMethod(method) { 20 // these HTTP methods do not require CSRF protection 21 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 22 } 23 24 $.ajaxSetup({ 25 beforeSend: function (xhr, settings) { 26 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 27 xhr.setRequestHeader("X-CSRFToken", csrftoken); 28 } 29 } 30 });
csrf相关装饰器
1 """ 2 1.网站整体都不校验csrf,就单单几个视图函数需要校验 3 2.网站整体都校验csrf,就单单几个视图函数不校验 4 """ 5 from django.views.decorators.csrf import csrf_protect,csrf_exempt 6 from django.utils.decorators import method_decorator 7 """ 8 csrf_protect 需要校验 9 针对csrf_protect符合我们之前所学的装饰器的三种玩法 10 csrf_exempt 忽视校验 11 针对csrf_exempt只能给dispatch方法加才有效 12 """ 13 # @csrf_exempt 14 # @csrf_protect 15 def transfer(request): 16 if request.method == 'POST': 17 username = request.POST.get('username') 18 target_user = request.POST.get('target_user') 19 money = request.POST.get('money') 20 print('%s给%s转了%s元'%(username,target_user,money)) 21 return render(request,'transfer.html') 22 23 24 25 from django.views import View 26 27 # @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式可以 28 # @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以 29 @method_decorator(csrf_exempt,name='dispatch') 30 class MyCsrfToken(View): 31 # @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以 32 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 33 def dispatch(self, request, *args, **kwargs): 34 return super(MyCsrfToken, self).dispatch(request,*args,**kwargs) 35 36 def get(self,request): 37 return HttpResponse('get') 38 39 # @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以 40 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以 41 def post(self,request): 42 return HttpResponse('post')
补充知识点
1 # 模块:importlib 2 import importlib 3 res = 'myfile.b' 4 ret = importlib.import_module(res) # from myfile import b 5 # 该方法最小只能到py文件名 6 print(ret)
切点函数: 1. 函数,在执行视图前被调用,每个请求都会调用,不主动进行返回或者返回HttpResponse对象 2. 函数,在调用视图之前执行,每个请求都会调用。不主动进行返回或者返回HttpResponse对象 3. 函数,在视图执行完之后执行,每个请求都会调用。不主动进行返回或者返回HttpResponse对象 4. 函数,所有响应返回浏览器之前调用,每个请求都会调用。不主动进行返回或者返回HttpResponse对象 5. process_exception 当视图抛出异常的时候调用,不主动进行返回或者返回HttpResponse对象 切面数据:函数传递进来的数据
实现方式:
1.定义一个类,继承MiddlewareMixin
2.重写切点函数
3.settings文件配置
中间件在注册的时候是一个列表,如果我们没有再切点处直接返回,中间件会依次执行,
如果直接进行返回,后续的中间件不再执行,因为已经被切断了。
基于django的重要编程思想
1 ''' 2 基于Django的编程思想,在配置文件的列表中配置字符串,就可以导入某个功能: 3 目录如下: 4 notify文件夹 5 __init__.py 6 email.py 7 qq.py 8 wechat.py 9 start.py 10 settings.py 11 12 ''' 13 14 # settings.py 15 import notify 16 17 path_str = [ 18 'notify.email.Email', 19 'notify.qq.QQ', 20 'notify.wechat.Wechat', 21 'notify.msn.Msn', 22 ] 23 24 ******************************************************************* 25 # email.py 其他的qq/wechat同 26 class Email(object): 27 def __init__(self): 28 pass # 发送qq前期需要的准备工作 29 30 def send(self,content): 31 print('邮箱发送的消息:%s' % content) 32 33 ******************************************************************* 34 # __init__.py 35 import settings 36 import importlib 37 38 def send_all(content): 39 for path_str in settings.NOTIFY_LIST: #'notify.email.Email' 40 module_path,class_name = path_str.rsplit('.',maxsplit=1) 41 # module_path = 'notify.email' class_name = 'Email' 42 # 1 利用字符串导入模块 43 module = importlib.import_module(module_path) # from notify import email 44 # 2 利用反射获取类名 45 cls = getattr(module,class_name) # Email、QQ、Wechat 46 # 3 生成类的对象 47 obj = cls() 48 # 4 利用鸭子类型直接调用send方法 49 obj.send(content) 50 51 ****************************************************************** 52 # start.py 53 import notify 54 55 notify.send_all('给我来把98K') 56
详情参考:https://www.cnblogs.com/yesirya/p/16071951.html
十一、auth模块
1 """ 2 其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表 3 django_session 4 auth_user 5 django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入 6 7 创建超级用户(管理员) 8 python3 manage.py createsuperuser 9 10 依赖于auth_user表完成用户相关的所有功能 11 """
Auth模块方法总结
1 from django.contrib import auth 2 # 1.比对用户名和密码是否正确 返回一个用户对象,如果不对返回None 3 user_obj = auth.authenticate(request,username=username,password=password) 4 # 括号内必须同时传入用户名和密码 5 print(user_obj) # 用户对象 jason 数据不符合则返回None 6 print(user_obj.username) # jason 7 print(user_obj.password) # 密文 8 9 # 2.保存用户状态 10 auth.login(request,user_obj) # 类似于request.session[key] = user_obj 11 # 主要执行了该方法 你就可以在任何地方通过request.user获取到当前登陆的用户对象 12 13 # 3.判断当前用户是否登陆 14 request.user.is_authenticated() 15 16 # 4.获取当前登陆用户 17 request.user 18 19 # 5.校验用户是否登陆装饰器 20 from django.contrib.auth.decorators import login_required 21 # 局部配置 22 @login_required(login_url='/login/') 23 # 全局配置 24 LOGIN_URL = '/login/' 25 1.如果局部和全局都有 该听谁的? 26 局部 > 全局 27 2.局部和全局哪个好呢? 28 全局的好处在于无需重复写代码 但是跳转的页面却很单一 29 局部的好处在于不同的视图函数在用户没有登陆的情况下可以跳转到不同的页面 30 31 # 6.比对原密码 32 is_right = request.user.check_password(old_password) # 自动加密比对原密码,返回值是布尔值 33 34 # 7.修改密码 35 request.user.set_password(new_password) # 仅仅是在修改对象的属性 36 request.user.save() # 这一步才是真正的操作数据库 37 38 # 8.注销 39 auth.logout(request) 40 41 # 9.注册 42 # 操作auth_user表写入数据 43 from django.contrib.auth.models import User 44 User.objects.create(username=username,password=password) # 写入数据 不能用create 密码没有加密处理 45 # 创建普通用户 46 User.objects.create_user(username=username,password=password) 47 # 创建超级用户(了解):使用代码创建超级用户 邮箱是必填的 而用命令创建则可以不填 48 User.objects.create_superuser(username=username,email='123@qq.com',password=password) 49 50 51 ********************************************************* 52 # views.py 53 54 55 from django.shortcuts import render,HttpResponse,redirect 56 from django.contrib import auth 57 from django.contrib.auth.decorators import login_required 58 from django.contrib.auth.models import User 59 60 def login(request): 61 if request.method == 'POST': 62 username = request.POST.get('username') 63 password = request.POST.get('password') 64 user_obj = auth.authenticate(request,username=username,password=password) 65 if user_obj: 66 auth.login(request,user_obj) 67 return redirect('/home/') 68 else: 69 return HttpResponse('用户名或者密码错误!') 70 return render(request,'login.html') 71 72 @login_required(login_url='/login/') 73 def home(request): 74 # print(request.user.is_authenticated()) # 校验用户是否登录,返回一个布尔值 75 return HttpResponse('home需要登录才能看啦!') 76 77 78 @login_required(login_url='/login/') 79 def set_pwd(request): # 修改密码 80 if request.method == 'POST': 81 old_password = request.POST.get('old_password') 82 new_password = request.POST.get('new_password') 83 confirm_password = request.POST.get('confirm_password') 84 if new_password == confirm_password: 85 is_right = request.user.check_password(old_password) 86 if is_right: 87 request.user.set_password(new_password) 88 request.user.save() 89 return redirect('/login/') 90 else: 91 return HttpResponse('原密码错误!请输入正确密码') 92 else: 93 return HttpResponse('两次密码不一致!') 94 return render(request,'set_pwd.html',locals()) 95 96 97 def register(request): 98 if request.method == 'POST': 99 username = request.POST.get('username') 100 password = request.POST.get('password') 101 conf_password = request.POST.get('conf_password') 102 if password == conf_password: 103 # 这种方法密码存的是明文的 104 # User.objects.create(username=username,password=password) 105 # 创建普通用户 密码密文 106 # User.objects.create_user(username=username,password=password) 107 # 创建超级用户 108 User.objects.create_superuser(username=username,email='lala',password=password) 109 return redirect('/login/') 110 else: 111 return HttpResponse('两次密码不一致!') 112 return render(request,'register.html') 113 114 @login_required(login_url='/login/') 115 def logout(request): 116 auth.logout(request) 117 return redirect('/login/')
如何扩展auth_user表
1.继承依赖
需要继承 AbstractUser 类
from django.contrib.auth.models import User,AbstractUser
2.前提
继承前没有执行过数据库迁移命令,这样执行数据库迁移命令的时候auth_user表就不会被创建,
继承的类里面不要覆盖AbstractUser里面的字段名,表里面有的字段都不要动,只扩展额外字段即可
# models.py
from django.contrib.auth.models import User,AbstractUser
class UserInfo(AbstractUser): phone = models.BigIntegerField() icon = models.FileField(upload_to='icon',default='xxx') # 只写扩展字段 不要动原有的字段
3.settings.py中配置
# settings.py AUTH_USER_MODEL = 'app01.UserInfo' # 应用名.表名
然后执行数据迁移命令,就ok了
汇总:
1 from django.db import models 2 from django.contrib.auth.models import User,AbstractUser 3 # Create your models here. 4 5 # 第一种:一对一关系 不推荐 6 # class UserDetail(models.Model): 7 # phone = models.BigIntegerField() 8 # user = models.OneToOneField(to='User') 9 10 11 # 第二种:面向对象的继承 12 class UserInfo(AbstractUser): 13 """ 14 如果继承了AbstractUser 15 那么在执行数据库迁移命令的时候auth_user表就不会再创建出来了 16 而UserInfo表中会出现auth_user所有的字段外加自己扩展的字段 17 这么做的好处在于你能够直接点击你自己的表更加快速的完成操作及扩展 18 19 前提: 20 1.在继承之前没有执行过数据库迁移命令 21 auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库 22 2.继承的类里面不要覆盖AbstractUser里面的字段名 23 表里面有的字段都不要动,只扩展额外字段即可 24 3.需要在配置文件settings.py中告诉django你要用UserInfo替代auth_user(******) 25 AUTH_USER_MODEL = 'app01.UserInfo' 26 '应用名.表名' 27 """ 28 phone = models.BigIntegerField() 29 30 31 """ 32 你如果自己写表替代了auth_user那么 33 auth模块的功能还是照常使用,参考的表页由原来的auth_user变成了UserInfo 34 35 36 我们bbs作业用户表就是用的上述方式 37 """
from django.contrib.auth.models import User,AbstractUser
document_root