课程介绍
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文件代码如下
1 from django.contrib import admin 2 from booktest.models import BookInfo,HeroInfo 3 4 class BookInfoAdmin(admin.ModelAdmin): 5 list_display = ['id', 'btitle', 'bpub_date'] 6 class HeroInfoAdmin(admin.ModelAdmin): 7 list_display = ['id', 'hname','hgender','hcomment'] 8 9 admin.site.register(BookInfo,BookInfoAdmin) 10 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。
条件查询
条件运算符
查询等
1 list=BookInfo.objects.filter(id__exact=1) 2 可简写为: 3 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对象
1 list = BookInfo.objects.filter(bread__gte=F('bcomment')) 2 list = BookInfo.objects.filter(bread__gt=F('bcomment') * 2)
Q对象
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字
1 list=BookInfo.objects.filter(bread__gt=20,id__lt=3) 2 或 3 list=BookInfo.objects.filter(bread__gt=20).filter(id__lt=3) 4 5 list = BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3)) 6 7 Q对象前可以使用~操作符,表示非not 8 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
位置参数
1 url(r'^delete(d+)/$',views.show_arg) 2 def show_arg(request,id): 3 return HttpResponse('show arg %s'%id)
关键字参数
1 url(r'^delete(?P<id1>d+)/$',views.show_arg) 2 def show_arg(request,id1): 3 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
1 from PIL import Image, ImageDraw, ImageFont 2 from django.utils.six import BytesIO 3 ... 4 def verify_code(request): 5 #引入随机函数模块 6 import random 7 #定义变量,用于画面的背景色、宽、高 8 bgcolor = (random.randrange(20, 100), random.randrange( 9 20, 100), 255) 10 width = 100 11 height = 25 12 #创建画面对象 13 im = Image.new('RGB', (width, height), bgcolor) 14 #创建画笔对象 15 draw = ImageDraw.Draw(im) 16 #调用画笔的point()函数绘制噪点 17 for i in range(0, 100): 18 xy = (random.randrange(0, width), random.randrange(0, height)) 19 fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) 20 draw.point(xy, fill=fill) 21 #定义验证码的备选值 22 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' 23 #随机选取4个值作为验证码 24 rand_str = '' 25 for i in range(0, 4): 26 rand_str += str1[random.randrange(0, len(str1))] 27 #构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont” 28 font = ImageFont.truetype('FreeMono.ttf', 23) 29 #构造字体颜色 30 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) 31 #绘制4个字 32 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) 33 draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) 34 draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) 35 draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) 36 #释放画笔 37 del draw 38 #存入session,用于做进一步验证 39 request.session['verifycode'] = rand_str 40 #内存文件操作 41 buf = BytesIO() 42 #将图片保存在内存中,文件类型为png 43 im.save(buf, 'png') 44 #将内存中的图片数据返回给客户端,MIME类型为图片png 45 return HttpResponse(buf.getvalue(), 'image/png')
验证
- /views.py文件中,创建视图verify_yz
1 def verify_yz(request): 2 yzm=request.POST.get('yzm') 3 verifycode=request.session['verifycode'] 4 response=HttpResponse('no') 5 if yzm==verifycode: 6 response=HttpResponse('ok') 7 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
1 from django.core.paginator import Paginator 2 from booktest.models import AreaInfo 3 ... 4 #参数pIndex表示:当前要显示的页码 5 def page_test(request,pIndex): 6 #查询所有的地区信息 7 list1 = AreaInfo.objects.filter(aParent__isnull=True) 8 #将地区信息按一页10条进行分页 9 p = Paginator(list1, 10) 10 #如果当前没有传递页码信息,则认为是第一页,这样写是为了请求第一页时可以不写页码 11 if pIndex == '': 12 pIndex = '1' 13 #通过url匹配的参数都是字符串类型,转换成int类型 14 pIndex = int(pIndex) 15 #获取第pIndex页的数据 16 list2 = p.page(pIndex) 17 #获取所有的页码信息 18 plist = p.page_range 19 #将当前页码、当前页的数据、页码信息传递到模板中 20 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>
示例:省市区选择
1 <html> 2 <head> 3 <title>省市区列表</title> 4 <script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script> 5 <script type="text/javascript"> 6 $(function(){ 7 //页面加载完成后获取省信息,并添加到省select 8 $.get('/area2/',function(dic) { 9 pro=$('#pro') 10 $.each(dic.data,function(index,item){ 11 pro.append('<option value='+item[0]+'>'+item[1]+'</option>'); 12 }) 13 }); 14 //为省select绑定change事件,获取市信息,并添加到市select 15 $('#pro').change(function(){ 16 $.get('/area3_'+$(this).val()+'/',function(dic){ 17 city=$('#city'); 18 city.empty().append('<option value="">请选择市</option>'); 19 dis=$('#dis'); 20 dis.empty().append('<option value="">请选择区县</option>'); 21 $.each(dic.data,function(index,item){ 22 city.append('<option value='+item[0]+'>'+item[1]+'</option>'); 23 }) 24 }); 25 }); 26 //为市select绑定change事件,获取区县信息,并添加到区县select 27 $('#city').change(function(){ 28 $.get('/area3_'+$(this).val()+'/',function(dic){ 29 dis=$('#dis'); 30 dis.empty().append('<option value="">请选择区县</option>'); 31 $.each(dic.data,function(index,item){ 32 dis.append('<option value='+item[0]+'>'+item[1]+'</option>'); 33 }) 34 }) 35 }); 36 37 }); 38 </script> 39 </head> 40 <body> 41 <select id="pro"> 42 <option value="">请选择省</option> 43 </select> 44 <select id="city"> 45 <option value="">请选择市</option> 46 </select> 47 <select id="dis"> 48 <option value="">请选择区县</option> 49 </select> 50 </body> 51 </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文件
1 import time 2 from celery import task 3 4 @task 5 def sayhello(): 6 print('hello ...') 7 time.sleep(2) 8 print('world ...')
- /views.py文件,修改sayhello视图
1 from booktest import tasks 2 ... 3 def sayhello(request): 4 # print('hello ...') 5 # time.sleep(2) 6 # print('world ...') 7 tasks.sayhello.delay() 8 return HttpResponse("hello world")
- 执行迁移生成celery需要的数据表
python manage.py migrate
- 启动Redis,如果已经启动则不需要启动
sudo service redis start
- 启动worker
python manage.py celery worker --loglevel=info
- 修改为发送邮件的代码,就可以实现无阻塞发送邮件
1 from django.conf import settings 2 from django.core.mail import send_mail 3 from celery import task 4 5 @task 6 def sayhello(): 7 msg='<a href="http://www.itcast.cn/subject/pythonzly/index.shtml" target="_blank">点击激活</a>' 8 send_mail('注册激活','',settings.EMAIL_FROM, 9 ['itcast88@163.com'], 10 html_message=msg)
请使用手机"扫一扫"x