项目中的 CSS、图片、JS 文件等都是静态文件。
在 settings 文件中定义静态内容:
STATIC_URL = '/static_virtual/' # 逻辑路径(供模板使用) STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), # 物理路径 ]
在项目根目录下创建 static 目录,再创建当前应用名称的目录:
DjangoDemo/static/hero_book/ # 根目录/static/应用目录
注意:因为请求静态文件实际上不需要走 Django 处理,因此不需要考虑 urls.py 的正则匹配规则,直接请求在服务器的真实路径即可。
{# 硬编码 #} <img src="/static/hero_info/example.jpg" /> {# 解析 static 逻辑路径 #} {% load static from staticfiles %} <img src="{ % static "my_app/myexample.jpg" %}" alt="My image"/>
执行结果:
Django 中间件是一个轻量级、底层的插件系统,可以介入 Django 的请求和响应处理过程,修改 Django 的输入或输出。
激活:添加到 Django 配置文件 settings.py 中的 MIDDLEWARE_CLASSES 元组中。
每个中间件组件是一个独立的Python类,可以定义下面方法中的一个或多个:
使用中间件,可以干扰整个处理过程,每次请求中都会执行中间件的这个方法。
1)与 settings.py 同级目录下创建 myexception.py 文件,定义类 MyException,实现 process_exception 方法:
from django.http import HttpResponse class MyException(): def process_exception(request,response, exception): return HttpResponse(exception.message)
2)将类 MyException 注册到 settings.py 中间件中:
MIDDLEWARE_CLASSES = ( 'hero_book.myexception.MyException', ... )
3)定义视图,并发生一个异常信息,则会运行自定义的异常处理。
当 Django 在处理文件上传的时候,文件数据被保存在 request.FILES。
将属性定义成 models.ImageField 类型:
pic=models.ImageField(upload_to='save_path/')
打开 settings.py 文件,增加 MEDIA_ROOT 项:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] MEDIA_ROOT = os.path.join(BASE_DIR, "static/media")
使用 Django 后台管理,遇到 ImageField 类型的属性会出现一个 file 框,完成文件上传。
手动上传的模板代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>文件上传</title> 6 </head> 7 <body> 8 <form method="post" action="/hero_book/upload_handler/" enctype="multipart/form-data"> 9 {% csrf_token %} 10 <input type="file" name="picture" /> 11 <input type="submit" value="上传"> 12 </form> 13 </body> 14 </html>
手动上传的视图代码:
1 def upload_handler(request): 2 received_pic = request.FILES["picture"] # 获取上传的图片 3 save_name = "%s/%s" % (settings.MEDIA_ROOT, received_pic) # 定义图片在服务器的保存目录 4 with open(save_name, "wb") as f: # 使用文件流保存图片 5 for c in received_pic.chunks(): 6 f.write(c) 7 # 返回显示图片的页面 8 return HttpResponse("<img src='/static/media/%s' />" % received_pic)
Django 提供了一些类实现管理数据分页,这些类位于 django/core/paginator.py 中。
Paginator(列表, int)
1 from django.conf import settings 2 from django.core.paginator import Paginator 3 4 5 def hero_list(request, p_index): # p_index 为页面点击的页码数 6 if p_index == "": # url没带页码数,则显示第一页数据 7 p_index = 1 8 hero_info = HeroInfo.objects.all() 9 paginator = Paginator(hero_info, 2) 10 page = paginator.page(int(p_index)) 11 return render(request, "hero_book/hero_list.html", {"page": page})
url(r'^hero_list/(\d*)/?$', views.hero_list),
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>英雄列表</title> 6 </head> 7 <body> 8 <ul> 9 {% for hero in page %} 10 <li>{{ hero.name }}</li> 11 {% endfor %} 12 </ul> 13 <hr/> 14 {% for page_index in page.paginator.page_range %} 15 {% if page_index == page.number %} {# 当前页码不需要链接 #} 16 {{page_index}} 17 {% else %} 18 <a href="/hero_book/hero_list/{{page_index}}/">{{page_index}}</a> 19 {% endif %} 20 {% endfor %} 21 </body> 22 </html>
使用视图通过上下文向模板中传递数据,需要先加载完成模板的静态页面,再执行模型代码,生成最终的 html 返回给浏览器,这个过程将页面与数据集成到了一起,扩展性差。
改进方案:通过 ajax 的方式获取数据,通过dom操作将数据呈现到界面上。
推荐使用框架的 ajax 相关方法,不使用 XMLHttpRequest 对象,因为操作麻烦且不容易查错。
jquery 框架中提供了 $.ajax、$.get、$.post 方法,用于进行异步交互。
由于 csrf 的约束,推荐使用 $.get。
最终实现效果如下图:
1 class AreaInfo(models.Model): 2 3 atitle = models.CharField(max_length=20) 4 aParent = models.ForeignKey('self', null=True, blank=True)
上级对象:area.aParent 下级对象:area.areainfo_set.all()
方式 1:使用迁移
python manage.py makemigrations python manage.py migrate
方式 2:直接在数据库新增表结构
create table book_info_areainfo( id int primary key, title varchar(20), parea_id int, foreign key(parea_id) references book_info_areainfo(id) -- 自关联 );
1 from django.shortcuts import render 2 from django.http.response import JsonResponse 3 from .models import BookInfo, HeroInfo, AreaInfo 4 5 6 # 首页:省市区ajax联动 7 def area(request): 8 return render(request, "hero_book/area.html") 9 10 # 省级数据 11 def area_handler(request): 12 province_list = AreaInfo.objects.filter(parea__isnull=True) # 返回省级数据 13 new_list = [] 14 for li in province_list: 15 # 将省级数据的id和名称加进新列表 16 new_list.append([li.id, li.title]) 17 return JsonResponse({"data": new_list}) 18 19 20 # 市级数据 21 def city(request, id): 22 city_list = AreaInfo.objects.filter(parea_id=id) # 根据省级编号获取对应的市级属 23 new_list = [] 24 for item in city_list: 25 #[{id:1, title:北京},{id:2, title:天津},...] 26 new_list.append({"id": item.id, "title": item.title}) 27 return JsonResponse({"data": new_list})
url(r'^area/$', views.area), url(r'^province/$', views.area_handler), url(r'^city(\d+)/$', views.city),
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>省市区ajax联动</title> 6 <script src="/static/hero_info/jquery-1.12.4.min.js"></script> 7 <script> 8 $(function(){ 9 // 查询省级信息 10 pro=$('#province') // 获取省级的下拉框,将获取到的模型数据添加进去 11 // "/hero_book/province/"为请求地址,function(dic)为回调函数,处理返回的数据。dic: {data :[[], [], []]} 12 $.get("/hero_book/province/", function(dic){ 13 $.each(dic.data, function(index, item){ // [id ,title] 14 // 返回的是列表,所以用item+索引方式获取编号和名称 15 pro.append("<option value='" + item[0] + "'>" + item[1] + "</option>") 16 }); 17 }); 18 19 // 查询市级信息 20 $('#province').change(function(){ 21 //city(\d+)/ 22 // select的value指:select中选中的option的value 23 $.get('/hero_book/city'+$(this).val()+"/", function(list){ 24 city = $("#city"); 25 // 每次需要先清空上一次的省选择结果,再重新添加option数据 26 city.empty().append('<option value="">请选择市</option>') 27 $("#district").empty().append('<option value="">请选择区县</option>') 28 // {data:#[{id:1, title:北京},{id:2, title:天津},...]} 29 $.each(list.data, function(index, item){ 30 // {id:1, title:"北京"} 31 // 返回的是json,所以用item.的方式获取编号和名称 32 city.append("<option value='" + item.id + "'>" + item.title + "</option>"); 33 }); 34 }); 35 }); 36 37 // 查询区级信息(可复用city视图函数) 38 $('#city').change(function(){ 39 //city(\d+)/ 40 $.get('/hero_book/city'+$(this).val()+"/", function(list){ 41 dis = $("#district"); 42 // 每次需要先清空上一次的省选择结果,再重新添加option数据 43 dis.empty().append('<option value="">请选择区县</option>') 44 // {data:#[{id:1, title:...},{id:2, title:...},...]} 45 $.each(list.data, function(index, item){ 46 // {id:1, title:"..."} 47 // 返回的是json,所以用item.的方式获取编号和名称 48 dis.append("<option value='" + item.id + "'>" + item.title + "</option>"); 49 }); 50 }); 51 }); 52 }); 53 </script> 54 </head> 55 <body> 56 <select id="province"> 57 <option value="">请选择省</option> 58 </select> 59 60 <select id="city"> 61 <option value="">请选择市</option> 62 </select> 63 64 <select id="district"> 65 <option value="">请选择区县</option> 66 </select> 67 </body> 68 </html>