课程介绍
MVC
MVC 框架的核心思想是: 解耦, 让不同的代码块之间降低耦合, 增强代码的可扩展性和可移植性, 实现向后兼容
M:Model, 主要封装对数据库层的访问, 对数据库中的数据进行增, 删, 改, 查操作
V:View, 用于封装结果, 生成页面展示的 HTML 内容
C:Controller, 用于接收请求, 处理业务逻辑, 与 Model 和 View 交互, 返回结果
Django-MVT
M:Model, 与 MVC 中的 M 功能相同, 负责和数据库交互, 进行数据处理
V:View, 与 MVC 中的 C 功能相同, 接收请求, 进行业务处理, 返回应答
T:Template, 与 MVC 中的 V 功能相同, 负责封装构造要返回的 HTML
MVVM
出现原因: 由于前端开发混合了 HTML,CSS 和 JavaScript, 而且页面众多, 所以, 代码的组织和维护难度其实更加复杂
好处: 改变 JavaScript 对象的状态, 会导致 DOM 结构作出对应的变化, 关注点就从如何操作 DOM 变成了如何更新 JavaScript 对象的状态
MVVM 的设计思想: 关注 Model 的变化, 让 MVVM 框架去自动更新 DOM 的状态, 从而把开发者从操作 DOM 的繁琐步骤中解脱出来
结构: 借鉴桌面应用程序的 MVC 思想, 在前端页面中, 把 Model 用纯 JavaScript 对象表示, View 负责显示, 两者做到了最大限度的分离
使用: 把 Model 和 View 关联起来的就是 ViewModel.ViewModel 负责把 Model 的数据同步到 View 显示出来, 还负责把 View 的修改同步回 Model
入门
设计模型
ORM 框架
O 是 object, 也就类对象的意思
R 是 relation, 翻译成中文是关系, 也就是关系数据库中数据表的意思
M 是 mapping, 是映射的意思
在 ORM 框架中, 它帮我们把类和数据表进行了一个映射, 可以让我们通过类和类对象就能操作它所对应的表格中的数据. ORM 框架还有一个功能, 它可以根据我们设计的类自动帮我们生成数据库中的表格, 省去了我们自己建表的过程
使用 django 进行数据库开发的步骤如下
在 models.py 中定义模型类
迁移
通过类和对象完成数据增删改查操作
说明: 不需要定义主键列, 在生成时会自动添加, 并且值为自动增长
数据表的默认名称为:<app_name>_<model_name>
一对多的关系 (ForeignKey) 应定义在多的那个类中
管理站点
注册模型类
登录后台管理后, 默认没有我们创建的应用中定义的模型类, 需要在自己应用中的 admin.py 文件中注册, 才可以在后台管理中看到, 并进行增删改查操作
自定义管理页面
Django 提供了自定义管理页面的功能, 比如列表页要显示哪些值
/admin.py 文件代码如下
- from django.contrib import admin
- from booktest.models import BookInfo,HeroInfo
- class BookInfoAdmin(admin.ModelAdmin):
- list_display = ['id', 'btitle', 'bpub_date']
- class HeroInfoAdmin(admin.ModelAdmin):
- list_display = ['id', 'hname','hgender','hcomment']
- admin.site.register(BookInfo,BookInfoAdmin)
- admin.site.register(HeroInfo,HeroInfoAdmin)
模型
数据库连接
/settings.py 文件, 找到 DATABASES 项, 默认使用 SQLite3 数据库
修改为使用 MySQL 数据库, 代码如下:
将引擎改为 MySQL, 提供连接的主机 HOST, 端口 PORT, 数据库名 NAME, 用户名 USER, 密码 PASSWORD.
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'test2', #数据库名字,
- 'USER': 'root', #数据库登录用户名
- 'PASSWORD': 'mysql', #数据库登录密码
- 'HOST': 'localhost', #数据库所在主机
- 'PORT': '3306', #数据库端口
- }
- }
注意: 数据库 test2 Django 框架不会自动生成, 需要我们自己进入 MySQL 数据库去创建
模型类
字段类型
使用时需要引入 django.db.models 包, 字段类型如下:
AutoField: 自动增长的 IntegerField, 通常不用指定, 不指定时 Django 会自动创建属性名为 id 的自动增长属性.
BooleanField: 布尔字段, 值为 True 或 False.
NullBooleanField: 支持 Null,True,False 三种值.
CharField(max_length = 字符长度): 字符串.
参数 max_length 表示最大字符个数.
TextField: 大文本字段, 一般超过 4000 个字符时使用.
IntegerField: 整数.
DecimalField(max_digits=None, decimal_places=None): 十进制浮点数.
参数 max_digits 表示总位数.
参数 decimal_places 表示小数位数.
FloatField: 浮点数.
DateField[auto_now=False, auto_now_add=False]): 日期.
参数 auto_now 表示每次保存对象时, 自动设置该字段为当前时间, 用于 "最后一次修改" 的时间戳, 它总是使用当前日期, 默认为 false.
参数 auto_now_add 表示当对象第一次被创建时自动设置当前时间, 用于创建的时间戳, 它总是使用当前日期, 默认为 false.
参数 auto_now_add 和 auto_now 是相互排斥的, 组合将会发生错误.
TimeField: 时间, 参数同 DateField.
DateTimeField: 日期时间, 参数同 DateField.
FileField: 上传文件字段.
ImageField: 继承于 FileField, 对上传的内容进行校验, 确保是有效的图片.
选项
通过选项实现对字段的约束, 选项如下:
null: 如果为 True, 表示允许为空, 默认值是 False.
blank: 如果为 True, 则该字段允许为空白, 默认值是 False.
对比: null 是数据库范畴的概念, blank 是表单验证范畴的.
db_column: 字段的名称, 如果未指定, 则使用属性的名称.
db_index: 若值为 True, 则在表中会为此字段创建索引, 默认值是 False.
default: 默认值.
primary_key: 若为 True, 则该字段会成为模型的主键字段, 默认值是 False, 一般作为 AutoField 的选项使用.
unique: 如果为 True, 这个字段在表中必须有唯一值, 默认值是 False.
条件查询
条件运算符
查询等
list=BookInfo.objects.filter(id__exact=1)
可简写为:
list=BookInfo.objects.filter(id=1)
模糊查询
说明: 如果要包含 % 无需转义, 直接写即可
contains: 是否包含
list = BookInfo.objects.filter(btitle__contains='传')
startswith,endswith: 以指定值开头或结尾
1 list = BookInfo.objects.filter(btitle__endswith='部')
以上运算符都区分大小写, 在这些运算符前加上 i 表示不区分大小写, 如 iexact,icontains,istartswith,iendswith
空查询
isnull: 是否为 null
list = BookInfo.objects.filter(btitle__isnull=False)
范围查询
in: 是否包含在范围内
1 list = BookInfo.objects.filter(id__in=[1, 3, 5])
比较查询
gt,gte,lt,lte: 大于, 大于等于, 小于, 小于等于
list = BookInfo.objects.filter(id__gt=3)
不等于的运算符, 使用 exclude()过滤器
1 list = BookInfo.objects.exclude(id=3)
日期查询
year,month,day,week_day,hour,minute,second: 对日期时间类型的属性进行运算
- list = BookInfo.objects.filter(bpub_date__year=1980)
- list = BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))
F 对象
两个属性比较使用 F 对象
- list = BookInfo.objects.filter(bread__gte=F('bcomment'))
- list = BookInfo.objects.filter(bread__gt=F('bcomment') * 2)
Q 对象
多个过滤器逐个调用表示逻辑与关系, 同 sql 语句中 where 部分的 and 关键字
list=BookInfo.objects.filter(bread__gt=20,id__lt=3)
或
- list=BookInfo.objects.filter(bread__gt=20).filter(id__lt=3)
- list = BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3))
Q 对象前可以使用~ 操作符, 表示非 not
list = BookInfo.objects.filter(~Q(pk=3))
聚合函数
使用 aggregate()过滤器调用聚合函数. 聚合函数包括: Avg,Count,Max,Min,Sum, 被定义在 django.db.models 中
list = BookInfo.objects.aggregate(Sum('bread'))
注意 aggregate 的返回值是一个字典类型, 格式如下:
{'聚合类小写__属性名': 值}
如:{'sum__bread':3}
使用 count 时一般不使用 aggregate()过滤器, count 函数的返回值是一个数字
list = BookInfo.objects.count()
查询集
返回查询集的过滤器
all(): 返回所有数据.
filter(): 返回满足条件的数据.
exclude(): 返回满足条件之外的数据, 相当于 sql 语句中 where 部分的 not 关键字.
order_by(): 对结果进行排序
返回单个值的过滤器
get(): 返回单个满足条件的对象
如果未找到会引发 "模型类. DoesNotExist" 异常.
如果多条被返回, 会引发 "模型类. MultipleObjectsReturned" 异常.
count(): 返回当前查询结果的总条数.
aggregate(): 聚合, 返回一个字典.
判断某一个查询集中是否有数据
exists(): 判断查询集中是否有数据, 如果有则返回 True, 没有则返回 False
两大特性
惰性执行: 创建查询集不会访问数据库, 直到调用数据时, 才会访问数据库, 调用数据的情况包括迭代, 序列化, 与 if 合用.
缓存: 使用同一个查询集, 第一次使用时会发生数据库的查询, 然后把结果缓存下来, 再次使用这个查询集时会使用缓存的数据
查询集的缓存
每个查询集都包含一个缓存来最小化对数据库的访问.
在新建的查询集中, 缓存为空, 首次对查询集求值时, 会发生数据库查询, django 会将查询的结果存在查询集的缓存中, 并返回请求的结果, 接下来对查询集求值将重用缓存中的结果.
限制查询集
可以对查询集进行取下标或切片操作, 等同于 sql 中的 limit 和 offset 子句.
注意: 不支持负数索引.
对查询集进行切片后返回一个新的查询集, 不会立即执行查询.
如果获取一个对象, 直接使用 [0], 等同于[0:1].get(), 但是如果没有数据,[0] 引发 IndexError 异常,[0:1].get()如果没有数据引发 DoesNotExist 异常.
示例: 获取第 1,2 项, 运行查看.
list=BookInfo.objects.all()[0:2]
模型类扩展
元选项
- # 定义图书模型类 BookInfo
- class BookInfo(models.Model):
- ...
- #定义元选项
- class Meta:
- db_table='bookinfo' #指定 BookInfo 生成的数据表名为 bookinfo
视图
URLconf
位置参数
- url(r'^delete(\d+)/$',views.show_arg)
- def show_arg(request,id):
- return HttpResponse('show arg %s'%id)
关键字参数
- url(r'^delete(?P<id1>\d+)/$',views.show_arg)
- def show_arg(request,id1):
- return HttpResponse('show %s'%id1)
HttpRequest 对象
属性
下面除非特别说明, 属性都是只读的
path: 一个字符串, 表示请求的页面的完整路径, 不包含域名和参数部分.
method: 一个字符串, 表示请求使用的 HTTP 方法, 常用值包括:'GET','POST'.
在浏览器中给出地址发出请求采用 get 方式, 如超链接.
在浏览器中点击表单的提交按钮发起请求, 如果表单的 method 设置为 post 则为 post 请求.
encoding: 一个字符串, 表示提交的数据的编码方式.
如果为 None 则表示使用浏览器的默认设置, 一般为 utf-8.
这个属性是可写的, 可以通过修改它来修改访问表单数据使用的编码, 接下来对属性的任何访问将使用新的 encoding 值.
GET:QueryDict 类型对象, 类似于字典, 包含 get 请求方式的所有参数.
POST:QueryDict 类型对象, 类似于字典, 包含 post 请求方式的所有参数.
FILES: 一个类似于字典的对象, 包含所有的上传文件.
COOKIES: 一个标准的 Python 字典, 包含所有的 cookie, 键和值都为字符串.
session: 一个既可读又可写的类似于字典的对象, 表示当前的会话, 只有当 Django 启用会话的支持时才可用, 详细内容见 "状态保持".
HttpResponse 对象
属性
content: 表示返回的内容.
charset: 表示 response 采用的编码字符集, 默认为 utf-8.
status_code: 返回的 HTTP 响应状态码.
content-type: 指定返回数据的的 MIME 类型, 默认为'text/html'.
方法
_init_: 创建 HttpResponse 对象后完成返回内容的初始化.
set_cookie: 设置 Cookie 信息.
set_cookie(key, value='', max_age=None, expires=None)
cookie 是网站以键值对格式存储在浏览器中的一段纯文本信息, 用于实现用户跟踪.
max_age 是一个整数, 表示在指定秒数后过期.
expires 是一个 datetime 或 timedelta 对象, 会话将在这个指定的日期 / 时间过期.
max_age 与 expires 二选一.
如果不指定过期时间, 在关闭浏览器时 cookie 会过期.
delete_cookie(key): 删除指定的 key 的 Cookie, 如果 key 不存在则什么也不发生.
write: 向响应体中写数据.
状态保持
浏览器请求服务器是无状态的.
无状态指一次用户请求时, 浏览器, 服务器无法知道之前这个用户做过什么, 每次请求都是一次新的请求.
无状态的应用层面的原因是: 浏览器和服务器之间的通信都遵守 HTTP 协议.
根本原因是: 浏览器与服务器是使用 Socket 套接字进行通信的, 服务器将请求结果返回给浏览器之后, 会关闭当前的 Socket 连接, 而且服务器也会在处理页面完毕之后销毁页面对象
有时需要保存下来用户浏览的状态, 比如用户是否登录过, 浏览过哪些商品等. 实现状态保持主要有两种方式
在客户端存储信息使用 Cookie
在服务器端存储信息使用 Session
状态保持
Cookie
指某些网站为了辨别用户身份, 进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)
Cookie 是由服务器端生成, 发送给 User-Agent(一般是浏览器), 浏览器会将 Cookie 的 key/value 保存到某个目录下的文本文件内, 下次请求同一网站时就发送该 Cookie 给服务器(前提是浏览器设置为启用 cookie)
Cookie 名称和值可以由服务器端开发自己定义, 这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等
服务器可以利用 Cookies 包含信息的任意性来筛选并经常性维护这些信息, 以判断在 HTTP 传输中的状态. Cookies 最典型记住用户名
Cookie 的特点
Cookie 以键值对的格式进行信息的存储
Cookie 基于域名安全, 不同域名的 Cookie 是不能互相访问的
当浏览器请求某网站时, 会将浏览器存储的跟网站相关的所有 Cookie 信息提交给网站服务器
设置 Cookie
response.set_cookie('h1', '你好')
读取 Cookie
- response.write('<h1>' + request.COOKIES['h1'] + '</h1>')
- Session
在服务器端进行状态保持的方案就是 Session
启用 Session
Django 项目默认启用 Session
/settings.py 文件, 在项 MIDDLEWARE_CLASSES 中启用 Session 中间件
存储方式
/settings.py 文件, 设置 SESSION_ENGINE 项指定 Session 数据存储的方式, 可以存储在数据库, 缓存, Redis 等
存储在数据库中, 如下设置可以写, 也可以不写, 这是默认存储方式
SESSION_ENGINE='django.contrib.sessions.backends.db'
存储在缓存中: 存储在本机内存中, 如果丢失则不能找回, 比数据库的方式读写更快
SESSION_ENGINE='django.contrib.sessions.backends.cache'
混合存储: 优先从本机内存中存取, 如果没有则从数据库中存取
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
如果存储在数据库中, 需要在项 INSTALLED_APPS 中安装 Session 应用
迁移后会在数据库中创建出存储 Session 的表
由表结构可知, 操作 Session 包括三个数据: 键, 值, 过期时间
依赖于 Cookie
所有请求者的 Session 都会存储在服务器中, 服务器如何区分请求者和 Session 数据的对应关系呢?
在使用 Session 后, 会在 Cookie 中存储一个 sessionid 的数据, 每次请求时浏览器都会将这个数据发给服务器, 服务器在接收到 sessionid 后, 会根据这个值找出这个请求者的 Session
结果: 如果想使用 Session, 浏览器必须支持 Cookie, 否则就无法使用 Session 了
存储 Session 时, 键与 Cookie 中的 sessionid 相同, 值是开发人员设置的键值对信息, 进行了 base64 编码, 过期时间由开发人员设置
对象及方法
通过 HttpRequest 对象的 session 属性进行会话的读写操作
以键值对的格式写 session
request.session['键']= 值
根据键读取值
request.session.get('键', 默认值)
清除所有 session, 在存储中删除值部分
request.session.clear()
清除 session 数据, 在存储中删除 session 的整条数据
request.session.flush()
删除 session 中的指定键及值, 在存储中只删除某个键及对应的值
del request.session['键']
设置会话的超时时间, 如果没有指定过期时间则两个星期后过期
设置会话的超时时间, 如果没有指定过期时间则两个星期后过期
value
如果 value 是一个整数, 会话将在 value 秒没有活动后过期
如果 value 为 0, 那么用户会话的 Cookie 将在用户的浏览器关闭时过期
如果 value 为 None, 那么会话永不过期
使用 Redis 存储 Session
会话还支持文件, 纯 cookie,Memcached,Redis 等方式存储
安装包
pip install django-Redis-sessions==0.5.6
/settings 文件, 增加如下项:
- SESSION_ENGINE = 'redis_sessions.session'
- SESSION_REDIS_HOST = 'localhost'
- SESSION_REDIS_PORT = 6379
- SESSION_REDIS_DB = 2
- SESSION_REDIS_PASSWORD = ''SESSION_REDIS_PREFIX ='session'
模板
模板语言
标签
for 标签语法如下:
{%for item in 列表 %}
循环逻辑
{{forloop.counter}}表示当前是第几次循环, 从 1 开始
{%empty%}
列表为空或不存在时执行此逻辑
{%endfor%}
if 标签语法如下:
{%if ...%}
逻辑 1
{%elif ...%}
逻辑 2
{%else%}
逻辑 3
{%endif%}
过滤器
变量 | 过滤器: 参数
长度 length, 返回字符串包含字符的个数, 或列表, 元组, 字典的元素个数
默认值 default, 如果变量不存在时则返回默认值
data|default:'默认值'
日期 date, 用于对日期类型的值进行字符串格式化, 常用的格式化字符如下:
Y 表示年, 格式为 4 位, y 表示两位的年.
m 表示月, 格式为 01,02,12 等.
d 表示日, 格式为 01,02 等.
j 表示日, 格式为 1,2 等.
H 表示时, 24 进制, h 表示 12 进制的时.
i 表示分, 为 0-59.
s 表示秒, 为 0-59.
value|date:"Y 年 m 月 j 日 H 时 i 分 s 秒"
HTML 转义
关闭转义
过滤器 escape 可以实现对变量的 HTML 转义, 默认模板就会转义, 一般省略
{{t1|escape}}
过滤器 safe: 禁用转义, 告诉模板这个变量是安全的, 可以解释执行
{{data|safe}}
标签 autoescape: 设置一段代码都禁用转义, 接受 on,off 参数
- {
- %autoescape off%
- }
- ...
- {
- %endautoescape%
- }
- CSRF
CSRF 全拼为 Cross Site Request Forgery, 译为跨站请求伪造. CSRF 指攻击者盗用了你的身份, 以你的名义发送恶意请求
验证码
手动实现验证码
/views.py 文件中, 创建视图 verify_code
随机生成字符串后存入 session 中, 用于后续判断
视图返回 mime-type 为 image/PNG
- from PIL import Image, ImageDraw, ImageFont
- from django.utils.six import BytesIO
- ...
- def verify_code(request):
- #引入随机函数模块
- import random
- #定义变量, 用于画面的背景色, 宽, 高
- bgcolor = (random.randrange(20, 100), random.randrange(
- 20, 100), 255)
- width = 100
- height = 25
- #创建画面对象
- im = Image.new('RGB', (width, height), bgcolor)
- #创建画笔对象
- draw = ImageDraw.Draw(im)
- #调用画笔的 point()函数绘制噪点
- for i in range(0, 100):
- xy = (random.randrange(0, width), random.randrange(0, height))
- fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
- draw.point(xy, fill=fill)
- #定义验证码的备选值
- str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
- #随机选取 4 个值作为验证码
- rand_str = ''
- for i in range(0, 4):
- rand_str += str1[random.randrange(0, len(str1))]
- #构造字体对象, Ubuntu 的字体路径为 "/usr/share/fonts/truetype/freefont"
- font = ImageFont.truetype('FreeMono.ttf', 23)
- #构造字体颜色
- fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
- #绘制 4 个字
- draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
- draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
- draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
- draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
- #释放画笔
- del draw
- #存入 session, 用于做进一步验证
- request.session['verifycode'] = rand_str
- #内存文件操作
- buf = BytesIO()
- #将图片保存在内存中, 文件类型为 PNG
- im.save(buf, 'png')
- #将内存中的图片数据返回给客户端, MIME 类型为图片 PNG
- return HttpResponse(buf.getvalue(), 'image/png')
验证
/views.py 文件中, 创建视图 verify_yz
- def verify_yz(request):
- yzm=request.POST.get('yzm')
- verifycode=request.session['verifycode']
- response=HttpResponse('no')
- if yzm==verifycode:
- response=HttpResponse('ok')
- return response
换验证码
- <script type="text/javascript" src="/static/jquery-1.12.4.min.js"></script>
- <script type="text/javascript">
- $(function(){
- $('#change').CSS('cursor','pointer').click(function() {
- $('#yzm').attr('src',$('#yzm').attr('src')+1)
- });
- });
- </script>
- ...
- <img id="yzm" src="/verify_code/?1"/>
- <span id="change">看不清, 换一个</span>
反向解析
在定义 url 时, 需要为 include 定义 namespace 属性, 为 url 定义 name 属性, 使用时, 在模板中使用 url 标签, 在视图中使用 reverse 函数, 根据正则表达式动态生成地址, 减轻后期维护成本
- url(r'^',include('booktest.urls',namespace='booktest'))
- url(r'^fan2/$', views.fan2,name='fan2')
反向解析:<a href="{%url'booktest:fan2'%}">fan2</a>
return redirect(reverse('booktest:fan2'))
常用
静态文件处理
/settings.py 文件中定义静态文件存放的物理目录
- STATIC_URL = '/static/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
分页
Paginator 类实例对象
方法_init_(列表, int): 返回分页对象, 第一个参数为列表数据, 第二个参数为每页数据的条数.
属性 count: 返回对象总数.
属性 num_pages: 返回页面总数.
属性 page_range: 返回页码列表, 从 1 开始, 例如[1, 2, 3, 4].
方法 page(m): 返回 Page 类实例对象, 表示第 m 页的数据, 下标以 1 开始
Page 类实例对象
调用 Paginator 对象的 page()方法返回 Page 对象, 不需要手动构造.
属性 object_list: 返回当前页对象的列表.
属性 number: 返回当前是第几页, 从 1 开始.
属性 paginator: 当前页对应的 Paginator 对象.
方法 has_next(): 如果有下一页返回 True.
方法 has_previous(): 如果有上一页返回 True.
方法 len(): 返回当前页面对象的个数.
示例
/views.py 文件中创建视图 page_test
- from django.core.paginator import Paginator
- from booktest.models import AreaInfo
- ...
- #参数 pIndex 表示: 当前要显示的页码
- def page_test(request,pIndex):
- #查询所有的地区信息
- list1 = AreaInfo.objects.filter(aParent__isnull=True)
- #将地区信息按一页 10 条进行分页
- p = Paginator(list1, 10)
- #如果当前没有传递页码信息, 则认为是第一页, 这样写是为了请求第一页时可以不写页码
- if pIndex == '':
- pIndex = '1'
- #通过 url 匹配的参数都是字符串类型, 转换成 int 类型
- pIndex = int(pIndex)
- #获取第 pIndex 页的数据
- list2 = p.page(pIndex)
- #获取所有的页码信息
- plist = p.page_range
- #将当前页码, 当前页的数据, 页码信息传递到模板中
- return render(request, 'booktest/page_test.html', {'list': list2, 'plist': plist, 'pIndex': pIndex})
/urls.py 文件中配置 url
url(r'^page(?P<pIndex>[0-9]*)/$', views.page_test)
创建 page_test.HTML 模板文件
- <HTML>
- <head>
- <title > 分页</title>
- </head>
- <body>
显示当前页的地区信息:<br>
- <ul>
- {%for area in list%}
- <li>
- {{area.id}}--{{area.atitle}}
- </li>
- {%endfor%}
- </ul>
- <hr>
显示页码信息: 当前页码没有链接, 其它页码有链接 < br>
- {%for pindex in plist%}
- {%if pIndex == pindex%}
- {{pindex}}
- {%else%}
- <a href="/page{{pindex}}/">{{pindex}}</a>
- {%endif%}
- {%endfor%}
- </body>
- </HTML>
示例: 省市区选择
- <HTML>
- <head>
- <title > 省市区列表</title>
- <script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script>
- <script type="text/javascript">
- $(function(){
- // 页面加载完成后获取省信息, 并添加到省 select
- $.get('/area2/',function(dic) {
- pro=$('#pro')
- $.each(dic.data,function(index,item){
- pro.append('<option value='+item[0]+'>'+item[1]+'</option>');
- })
- });
- // 为省 select 绑定 change 事件, 获取市信息, 并添加到市 select
- $('#pro').change(function(){
- $.get('/area3_'+$(this).val()+'/',function(dic){
- city=$('#city');
- city.empty().append('<option value="">请选择市</option>');
- dis=$('#dis');
- dis.empty().append('<option value="">请选择区县</option>');
- $.each(dic.data,function(index,item){
- city.append('<option value='+item[0]+'>'+item[1]+'</option>');
- })
- });
- });
- // 为市 select 绑定 change 事件, 获取区县信息, 并添加到区县 select
- $('#city').change(function(){
- $.get('/area3_'+$(this).val()+'/',function(dic){
- dis=$('#dis');
- dis.empty().append('<option value="">请选择区县</option>');
- $.each(dic.data,function(index,item){
- dis.append('<option value='+item[0]+'>'+item[1]+'</option>');
- })
- })
- });
- });
- </script>
- </head>
- <body>
- <select id="pro">
- <option value="">请选择省</option>
- </select>
- <select id="city">
- <option value="">请选择市</option>
- </select>
- <select id="dis">
- <option value="">请选择区县</option>
- </select>
- </body>
- </HTML
- celery
情景: 用户发起 request, 并等待 response 返回. 在本些 views 中, 可能需要执行一段耗时的程序, 那么用户就会等待很长时间, 造成不好的用户体验, 比如发送邮件, 手机验证码等
celery 名词
任务 task: 就是一个 Python 函数
队列 queue: 将需要执行的任务加入到队列中
工人 worker: 在一个新进程中, 负责执行队列中的任务
代理人 broker: 负责调度, 在布置环境中使用 Redis
/settings.py 中安装
- INSTALLED_APPS = (
- ...
- 'djcelery',
- }
/settings.py 文件中配置代理和任务模块
- import djcelery
- djcelery.setup_loader()
- BROKER_URL = 'redis://127.0.0.1:6379/2'
创建 tasks.py 文件
- import time
- from celery import task
- @task
- def sayhello():
- print('hello ...')
- time.sleep(2)
- print('world ...')
/views.py 文件, 修改 sayhello 视图
- from booktest import tasks
- ...
- def sayhello(request):
- # print('hello ...')
- # time.sleep(2)
- # print('world ...')
- tasks.sayhello.delay()
- return HttpResponse("hello world")
执行迁移生成 celery 需要的数据表
python manage.py migrate
启动 Redis, 如果已经启动则不需要启动
sudo service Redis start
启动 worker
python manage.py celery worker --loglevel=info
修改为发送邮件的代码, 就可以实现无阻塞发送邮件
- from django.conf import settings
- from django.core.mail import send_mail
- from celery import task
- @task
- def sayhello():
- msg='<a href="http://www.itcast.cn/subject/pythonzly/index.shtml"target="_blank">点击激活</a>'
- send_mail('注册激活','',settings.EMAIL_FROM,
- ['itcast88@163.com'],
- html_message=msg)
请使用手机 "扫一扫"x
来源: https://www.cnblogs.com/siplips/p/9873562.html