5- 常用
常用
- 到此为止,关于Django框架的三大块M、V、T已经全部讲完了,接下来学习一些其它的知识点,这些知识点不在三大块范围内,Django提供了这些功能后,可以帮助我们更快更好的完成开发
- 主要知识点如下:
- 静态文件处理
- 中间件
- 上传图片
- Admin站点
- 分页
- 示例:省市区选择,jquery,ajax
创建示例项目
- 在pycharm中创建项目test5,创建应用booktest,使用mysql数据库
- 打开booktest/models.py文件,定义模型类AreaInfo,结构参照第二天设计
#coding=utf-8 from django.db import models #定义地区模型类,存储省、市、区县信息 class AreaInfo(models.Model): atitle=models.CharField(max_length=30)#名称 aParent=models.ForeignKey('self',null=True,blank=True)#父级
静态文件
-
项目中的CSS、图片、js都是静态文件
-
一般会将静态文件放到一个单独的目录中,以方便管理
-
在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径
-
静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理
示例
- 在test5/settings.py文件中定义静态文件查找路径
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
- 在项目根目录下创建static目录,再创建img、css、js目录,结构如下图

- 在booktest/views.py中定义视图jingtai
def jingtai(request):
return render(request,'booktest/jingtai.html')
- 在booktest/urls.py中配置url
url(r'^jingtai/$',views.jingtai),
- 在templates/booktest/下创建jingtai.html文件
<html>
<head>
<title>静态文件</title>
</head>
<body>
<img src="/static/img/sg.png"/>
</body>
</html>
- 保存图片到static/img/目录下,名称为sg.png,目录结构如下

- 运行服务器,浏览效果如下图

配置静态文件
-
Django提供了一种配置,可以在html页面中可以隐藏真实路径
-
在test5/settings.py文件中修改STATIC_URL项
# STATIC_URL = '/static/' STATIC_URL = '/abc/'
- 刷新浏览器,图片找不到了,效果如下图

- 修改templates/booktest/jingtai.html如下
<html>
<head>
<title>静态文件</title>
</head>
<body>
修改前:<img src="/static/img/sg.png"/>
<hr>
修改后:<img src="/abc/img/sg.png"/>
</body>
</html>
- 刷新浏览器,效果如下图

- 查看网页源代码,发现可以网址和真实地址之间没有关系

-
为了安全可以通过配置项隐藏真实图片路径,在模板中写成固定路径,后期维护太麻烦,可以使用static标签,根据配置项生成静态文件路径
-
修改templates/booktest/jingtai.html如下
<html>
<head>
<title>静态文件</title>
</head>
<body>
修改前:<img src="/static/img/sg.png"/>
<hr>
修改后:<img src="/abc/img/sg.png"/>
<hr>
动态配置:
{%load static from staticfiles%}
<img src="{%static "img/sg.png" %}"/>
</body>
</html>
- 刷新浏览器,效果如下图

- 查看网页源代码如下图

- 说明:这种方案可以隐藏真实的静态文件路径,但是结合Nginx布署时,会将所有的静态文件都交给Nginx处理,而不用转到Django部分,所以这项配置就无效了,这个功能有些鸡肋
中间件
-
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出
-
中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性,其它的MVC框架也有这个功能,名称为IoC
-
Django在中间件中预置了六个方法,这六个方法的区别在于不同的阶段执行,对输入或输出进行干预,方法如下
-
1.初始化:无需任何参数,服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件
def __init__():
pass
- 2.处理请求前:在每个请求上调用,返回None或HttpResponse对象
def process_request(request): pass
- 3.处理视图前:在每个请求上调用,返回None或HttpResponse对象
def process_view(request, view_func, view_args, view_kwargs): pass
- 4.处理模板响应前:在每个请求上调用,返回实现了render方法的响应对象
def process_template_response(request, response):
pass
- 5.处理响应后:所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象
def process_response(request, response): pass
- 6.异常处理:当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象
def process_exception(request,exception): pass
- 下图描述了六个方法的一次请求响应过程中的阶段
示例
-
中间件是一个独立的python类,,可以定义这五个方法中的一个或多个
-
在booktest/目录下创建middleware.py文件,代码如下
#coding=utf-8 class my_mid: def __init__(self): print '--------------init' def process_request(self,request): print '--------------request' def process_view(self,request, view_func, view_args, view_kwargs): print '--------------view' def process_template_response(self,request, response): print '--------------template' return response def process_response(self,request, response): print '--------------response' return response
- 在test5/settings.py文件中,向MIDDLEWARE_CLASSES项中注册

-
修改booktest/views.py中视图index
def index(request): print '======index======' return render(request,'booktest/index.html')
- 运行服务器,命令行中效果如下图

- 刷新页面,命令行中效果如下图

异常中间件
- 在booktest/middleware.py中定义两个异常类如下
class exp1: def process_exception(self,request,exception): print '--------------exp1' class exp2: def process_exception(self,request,exception): print '--------------exp2'
- 在test5/settings.py文件中,向MIDDLEWARE_CLASSES项中注册

-
修改booktest/views.py中视图index
def index(request):
print '======index======'
raise Exception('自定义异常')
return render(request,'booktest/index.html')
- 总结:如果多个中间件中注册了相同的方法,则先注册的后执行
Admin站点
-
内容发布的部分由网站的管理员负责查看、添加、修改、删除数据,开发这些重复的功能是一件单调乏味、缺乏创造力的工作,为此,Django能够根据定义的模型类自动地生成管理模块
-
在第一部分对管理站点做了简单介绍,现在做详细讲解
-
在Django项目中默认启用Admin管理站点
-
准备工作:创建管理员的用户名和密码
python manage.py createsuperuser
- 按提示填写用户名、邮箱、密码

-
使用:在应用的admin.py中注册模型类
-
例:打开booktest/admin.py文件,注册地区模型
from django.contrib import admin from models import * admin.site.register(AreaInfo)
- 输入如下网址
http://127.0.0.1:8000/admin/
- 按提示填写用户名、密码,点击“Log in”按钮登录

- 登录成功后,可以看到AreaInfos,可以进行增加、修改、删除、查询的管理

- 列表页显示效果如下图

控制管理页展示
-
类ModelAdmin可以控制模型在Admin界面中的展示方式,主要包括在列表页的展示方式、添加修改页的展示方式
-
在booktest/admin.py中,注册模型类前定义管理类AreaAdmin
class AreaAdmin(admin.ModelAdmin): pass
-
管理类有两种使用方式注册参数:打开booktest/admin.py文件,注册模型类代码如下
- 注册参数
- 装饰器
admin.site.register(AreaInfo,AreaAdmin)
- 装饰器:打开booktest/admin.py文件,在管理类上注册模型类,代码如下
@admin.register(AreaInfo) class AreaAdmin(admin.ModelAdmin): pass
- 接下来介绍如何控制列表页、增加修改页展示效果
列表页选项
页大小
- 每页中显示多少条数据,默认为每页显示100条数据,属性如下
list_per_page=100
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin): list_per_page = 10
- 在浏览器中查看区域信息的列表页面,效果如下图

“操作选项”的位置
- 顶部显示的属性,设置为True在顶部显示,设置为False不在顶部显示,默认为True
actions_on_top=True
- 底部显示的属性,设置为True在底部显示,设置为False不在底部显示,默认为False
actions_on_bottom=False
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin): ... actions_on_top = True actions_on_bottom = True
- 在浏览器中刷新效果如下图

列表中的列
- 属性如下
list_display=[模型字段1,模型字段2,...]
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
list_display = ['id','atitle']
- 在浏览器中刷新效果如下图

- 点击列头可以进行升序或降序排列
将方法作为列
-
列可以是模型字段,还可以是模型方法,要求方法有返回值
-
打开booktest/models.py文件,修改AreaInfo类如下
class AreaInfo(models.Model): ... def title(self): return self.atitle
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
list_display = ['id','atitle','title']
- 在浏览器中刷新效果如下图

- 方法列是不能排序的,如果需要排序需要为方法指定排序依据
admin_order_field=模型类字段
- 打开booktest/models.py文件,修改AreaInfo类如下
class AreaInfo(models.Model):
...
def title(self):
return self.atitle
title.admin_order_field='atitle'
- 在浏览器中刷新效果如下图

列标题
-
列标题默认为属性或方法的名称,可以通过属性设置
-
需要先将模型字段封装成方法,再对方法使用这个属性,模型字段不能直接使用这个属性
short_description='列标题'
- 打开booktest/models.py文件,修改AreaInfo类如下
class AreaInfo(models.Model):
...
title.short_description='区域名称'
- 在浏览器中刷新效果如下图

关联对象
-
无法直接访问关联对象的属性或方法,可以在模型类中封装方法,访问关联对象的成员
-
打开booktest/models.py文件,修改AreaInfo类如下
class AreaInfo(models.Model): ... def parent(self): return self.aParent.atitle parent.short_description='父级区域名称'
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin): ... list_display = ['id','atitle','title','parent']
- 在浏览器中刷新效果如下图

右侧栏过滤器
- 属性如下,只能接收字段,会将对应字段的值列出来,用于快速过滤
list_filter=[]
- 一般用于有重复值的字段
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
list_filter=['atitle']
- 在浏览器中刷新效果如下图

搜索框
- 属性如下,用于对指定字段的值进行搜索,支持模糊查询
search_fields=[]
-
列表类型,表示在这些字段上进行搜索
-
打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
search_fields=['atitle']
- 在浏览器中刷新效果如下图

中文标题
- 打开booktest/models.py文件,修改模型类,为属性指定verbose_name参数,即第一个参数
class AreaInfo(models.Model): atitle=models.CharField('标题',max_length=30)#名称 ...
- 在浏览器中刷新效果如下图

编辑页选项
显示字段顺序
- 属性如下
fields=[]
- 点击某行ID的链接,可以转到修改页面,默认效果如下图

- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin): ... fields=['aParent','atitle']
- 刷新浏览器效果如下图

-
在下拉列表中输出的是对象的名称,可以在模型类中定义str方法用于对象转换字符串
-
打开booktest/models.py文件,修改AreaInfo类,添加str方法
class AreaInfo(models.Model): ... def __str__(self): return self.atitle
- 刷新浏览器效果如下图

分组显示
- 属性如下
fieldset=( ('组1标题',{'fields':('字段1','字段2')}), ('组2标题',{'fields':('字段3','字段4')}), )
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin): ... # fields=['aParent','atitle'] fieldsets = ( ('基本', {'fields': ['atitle']}), ('高级', {'fields': ['aParent']}) )
- 刷新浏览器效果如下图

- 说明:fields与fieldsets两者选一使用
关联对象
-
在一对多的关系中,可以在一端的编辑页面中编辑多端的对象,嵌入多端对象的方式包括表格、块两种
-
类型InlineModelAdmin:表示在模型的编辑页面嵌入关联模型的编辑
-
子类TabularInline:以表格的形式嵌入
-
子类StackedInline:以块的形式嵌入
-
打开booktest/admin.py文件,创建AreaStackedInline类
class AreaStackedInline(admin.StackedInline):
model = AreaInfo#关联子对象
extra = 2#额外编辑2个子对象
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
inlines = [AreaStackedInline]
- 刷新浏览器效果如下图

-
可以用表格的形式嵌入
-
打开booktest/admin.py文件,创建AreaTabularInline类
class AreaTabularInline(admin.TabularInline):
model = AreaInfo#关联子对象
extra = 2#额外编辑2个子对象
- 打开booktest/admin.py文件,修改AreaAdmin类如下
class AreaAdmin(admin.ModelAdmin):
...
inlines = [AreaTabularInline]
- 刷新浏览器效果如下图

重写模板
- 在templates/目录下创建admin目录,结构如下图

- 打开当前虚拟环境中Django的目录,再向下找到admin的模板,目录如下
/home/python/.virtualenvs/py_django/lib/python2.7/site-packages/django/contrib/admin/templates/admin
- 将需要更改文件拷贝到第一步建好的目录里,此处以base_site.html为例

- 编辑base_site.html文件
{% extends "admin/base.html" %}
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
<hr>
<h1>自定义的管理页模板</h1>
<hr>
{% endblock %}
{% block nav-global %}{% endblock %}
- 在浏览器中转到列表页面,刷新后如下图

- 其它后台的模板可以按照相同的方式进行修改
上传图片
- 在python中进行图片操作,需要安装包PIL
pip install Pillow==3.4.1
- 在Django中上传图片包括两种方式:
-
在管理页面admin中上传图片
-
自定义form表单中上传图片
-
- 上传图片后,将图片存储在服务器上,然后将图片的路径存储在表中
创建包含图片的模型类
-
将模型类的属性定义成models.ImageField类型
-
打开booktest/models.py文件,定义模型类PicTest
class PicTest(models.Model): pic = models.ImageField(upload_to='booktest/')
- 回到命令行中,生成迁移
python manage.py makemigrations

- 回到命令行中,执行迁移
python manage.py migrate
- 因为当前没有定义图书、英雄模型类,会提示“是否删除”,输入“no”后回车,表示不删除

-
打开test5/settings.py文件,设置图片保存路径
-
因为图片也属于静态文件,所以保存到static目录下
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
- 在static目录下创建media目录,再创建应用名称的目录,此例为booktest

在管理页面admin中上传图片
- 打开booktest/admin.py文件,注册PicTest
from django.contrib import admin from models import * admin.site.register(PicTest)
- 运行服务器,输入如下网址
http://127.0.0.1:8000/admin/

点击“Add”添加数据,打开新页

-
选择图片,点击“save”按钮完成图片上传
-
回到数据库命令行,查询表pictest中的数据如下图

- 图片被保存到目录static/media/booktest/下,如下图

自定义form表单中上传图片
- 打开booktest/views.py文件,创建视图pic_upload
def pic_upload(request): return render(request,'booktest/pic_upload.html')
- 打开booktest/urls.py文件,配置url
url(r'^pic_upload/$', views.pic_upload),
-
在templates/booktest/目录下创建模板pic_upload.html
-
在模板中定义上传表单,要求如下
-
form的属性enctype="multipart/form-data"
-
form的method为post
-
input的类型为file
-
<html>
<head>
<title>自定义上传图片</title>
</head>
<body>
<form method="post" action="/pic_handle/" enctype="multipart/form-data">
{%csrf_token%}
<input type="file" name="pic"/><br>
<input type="submit" value="上传">
</form>
</body>
</html>
-
打开booktest/views.py文件,创建视图pic_handle,用于接收表单保存图片
-
request对象的FILES属性用于接收请求的文件,包括图片
import os from django.core.files.storage import FileSystemStorage ... def pic_handle(request): f1=request.FILES.get('pic') f2=FileSystemStorage() r=f2.save('booktest/%s'%(f1.name),f1)#返回文件名,如果文件名存在则会创建一个不重复的名称 p1=PicTest() p1.pic=r p1.save() return HttpResponse('OK')
- 打开booktest/urls.py文件,配置url
url(r'^pic_handle/$', views.pic_handle),
- 运行服务器,在浏览器中输入如下网址
http://127.0.0.1:8000/pic_upload/

-
选择文件后点击按钮上传图片
-
图片上传目录如下图

- 这里只是完成图片上传的代码,如果需要保存数据需要创建PicTest对象完成保存
显示图片
- 打开booktest/views.py文件,创建视图pic_show
from models import PicTest
...
def pic_show(request):
pic=PicTest.objects.get(pk=1)
context={'pic':pic}
return render(request,'booktest/pic_show.html',context)
- 打开booktest/urls.py文件,配置url
url(r'^pic_show/$', views.pic_show),
- 在templates/booktest/目录下创建模板pic_show.html
<html>
<head>
<title>显示上传的图片</title>
</head>
<body>
<img src="/static/media/{{pic.pic}}"/>
</body>
</html>
- 运行服务器,在浏览器中输入如下网址
http://127.0.0.1:8000/pic_show/

分页
-
Django提供了数据分页的类,这些类被定义在django/core/paginator.py中
-
对象Paginator用于对列进行一页n条数据的分页运算
-
对象Page用于表示第m页的数据
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():返回当前页面对象的个数
-
迭代页面对象:访问当前页面中的每个对象
示例
- 在booktest/views.py文件中创建视图page_test
from django.core.paginator import Paginator from 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})
- 在booktest/urls.py文件中配置url
url(r'^page(?P<pIndex>[0-9]*)/$', views.page_test),
- 在templates/booktest/目录下创建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>
- 运行服务器,在浏览器中输入如下网址
http://127.0.0.1:8000/page/

- 点击页码数字,效果如下图

示例:省市区选择
-
本示例讲解在Django中使用jquery的ajax进行数据交互
-
jquery框架中提供了$.ajax、$.get、$.post方法,用于进行异步交互,由于Django中默认使用CSRF约束,推荐使用$.get
-
示例:实现省市区的选择
-
最终实现效果如图:

- 将jquery文件拷贝到static/js/目录下

- 打开booktest/views.py文件,定义视图area1,用于显示下拉列表
#提供显示下拉列表的控件,供用户操作 def area1(request): return render(request,'booktest/area1.html')
- 打开booktest/urls.py文件,配置url
url(r'^area1/$', views.area1),
- 在templates/booktest/目录下创建area1.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').empty().append('<option value="">请选择区县</option>');
$.each(dic.data,function(index,item){
city.append('<option value='+item.id+'>'+item.title+'</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.id+'>'+item.title+'</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>
- 运行服务器,在浏览器中输入如下网址
http://127.0.0.1:8000/area1/
- 浏览效果如下图

- 打开booktest/views.py文件,定义视图area2,用于获取省信息
from django.http import JsonResponse ... #获取省信息 def area2(request): list = AreaInfo.objects.filter(aParent__isnull=True) list2 = [] for item in list: list2.append([item.id, item.atitle]) return JsonResponse({'data': list2})
- 打开booktest/urls.py文件,配置url
url(r'^area2/$', views.area2),
- 在浏览器中输入如下网址
http://127.0.0.1:8000/area2/
- 浏览效果如下图

- 打开booktest/views.py文件,定义视图area3,用于根据编号获取对应的子级信息,如果传递的是省编号则获取市信息,如果传递的是市编号则获取区县信息
#根据pid查询子级区域信息 def area3(request, pid): list = AreaInfo.objects.filter(aParent_id=pid) list2 = [] for item in list: list2.append({'id': item.id, 'title': item.atitle}) return JsonResponse({'data': list2})
- 打开booktest/urls.py文件,配置url
url(r'^area3_(\d+)/$', views.area3),
- 在浏览器中输入如下网址
http://127.0.0.1:8000/area3_140000/
- 浏览效果如下图

- 在浏览器中输入如下网址
http://127.0.0.1:8000/area1/
- 选择效果如下图

总结
- 静态文件
- 中间件
- Admin站点
- 上传图片
- 分页
- 示例:省市区选择,使用ajax实现

浙公网安备 33010602011771号