django框架之路由层 视图层......
Django 框架结构
urlspy 路由层
views.py 视图层
templates 模板层
models.py 模型层
路由层
数据处理结果request,是由wsgiref完成
1. 路由匹配
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^$', views.home), # 网站首页路由 url(r'test/$', views.test), # 以test/结尾 url(r'testadd/$', views.testadd), # 以testadd/结尾 url(r'', views.error) # 网站不存在页面,该路由存在,网页将不会自动加/重定向 ]
url('正则表达式',“视图函数内存地址”)
url(r'^index.html',views.index)
url(r'^index/\d+.html',views.index)
url(r'^index/[0-9]{4}/',views.index)
第一个参数是正则表达式,匹配规则按照从上往下依次匹配,匹配到一个后立即执行对应的函数,不再继续往下匹配
路由过多的时候,可能在它之前就有路由匹配上,目标函数将不会被执行,一可以换前后位置,二换新的路由
PS:路由匹配到的数据默认都是字符串形式
1)分组
无名分组:将加括号的正则表达式匹配到的内容,当做位置参数自动传递给对应的视图函数
有名分组:将加括号的正则表达式匹配到的内容,当做关键字参数自动传递给对应的视图函数
注意:无名分组和有名分组不支持混合使用!!!
但是支持用一类型多个分组匹配
urlpatterns = [ url(r'^admin/', admin.site.urls),
url(r'^admin/([0-9]{4})/', admin.site.urls), # 无名分组, url(r'^test/\d+/', views.test), # 匹配一个或多个数字,只要有数字访问不受影响 url(r'^test/(\d+)/', views.test1), # 无名分组,会将\d+当做位置参数传递给函数 url(r'^test/(\d+)/(\d+)/', views.test11), # 无名分组多个 url(r'^test/(?P<year>\d+)/', views.test2), # 有名分组,会将\d+当做关键字参数传递给函数 url(r'^test/(?P<year>\d+)/(?P<xx>\d+)/', views.test22) # 有名分组多个 ]
def test(request): return HttpResponse('test') def test1(request, xx): print(xx) return HttpResponse('test1') def test11(request, xx, yy): # 形参可以任意表示,但数量相等 print(xx) print(yy) return HttpResponse('test11') def test2(request, year): print(year) return HttpResponse('test2') def test22(request, xx, year): # 形参必须与路由相同,位置任意 print('xx:', xx) print('year:', year) return HttpResponse('test22')
2)反向解析
视图层根据名字动态获取该别名所对应的能够匹配url的路径 from django.shortcuts import reverse
可以给每一个路由与视图函数对应关系起一个别名
这个名字能够唯一标识出对应的路径
注意:这个名字不能重复是唯一的
本质:获取到能够访问名字所对应的视图函数
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/$', views.index, name='index'), # 路由与视图函数的关系取别名 index url(r'^index1/$', views.index1, name='index1'), # 别名 index1 url(r'^test3/(\d+)/', views.test3, name='test3'), # 别名 test3
url(r'^test4/(\d+)/(\d+)/(\d+)/', views.test4, name='test4'), # 别名 test4
]
from django.shortcuts import render,HttpResponse,reverse def index(request): print('url:', reverse('index')) return render(request, 'index.html') def index1(request): print(reverse('test3', args=(1,))) # test3 需要一个无名分组参数,不传参报错 return render(request, 'index1.html') def test3(request, xx): print('xx:', xx) print('url:', reverse('index1')) return HttpResponse('test3')
def test4(request, *args):
print(reverse('test4', args=args)) # /test4/599/288/246/
return HttpResponse(*args) # 599 ??
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> <a href="{% url 'index' %}"></a> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <a href="{% url 'test3' 10 %}"></a> <a href="{% url 'test3' 10 %}"></a> <a href="{% url 'test3' 10 %}"></a> <a href="{% url 'test3' 10 %}"></a> <a href="{% url 'test3' 10 %}"></a> <a href="{% url 'index1' %}"></a> <a href="{% url 'index1' %}"></a> <a href="{% url 'index1' %}"></a> <a href="{% url 'index1' %}"></a> </body> </html>
# 反向解析(根据名字动态获取到对应路径) url(r'^index6668888/$',views.index,name='index') 前端使用 {% url '别名' %} 后端使用 from django.shortcuts import reverse reverse('index') # 无名分组反向解析 url(r'^test/(\d+)/',views.test,name='list') 后端使用 reverse('list',args=(10,)) 前端使用 {% url 'list' user_obj.pk %} # 有名分组反向解析 后端使用 reverse('list',args=(10,)) # 后端有名分组和无名分组都可以用这种形式 reverse('list',kwargs={'year':10}) # 下面这个了解即可 前端使用 {% url 'list' user_obj.pk %} # 前端有名分组和无名分组都可以用这种形式 {% url 'list' xxx=user_obj.pk %} # 下面这个了解即可
# 总结:针对有名分组与无名分组的反向解析统一采用一种格式即可
后端 reverse('list',args=(10,)) # 这里的数字通常都是数据的主键值
前端 {% url 'list' user_obj.pk %}
实例
展示数据: user_list = models.User.objects.all() url(r'^edit/(\d+)', views.edit, name='edit') 前端模板语法 {% for user_obj in user_list %} <a href='edit/{{ user_obj.pk }}/'></a>
{% endfor %}
增加编辑按钮: 视图函数 from django.shortcuts import reverse def edit(request, edit_id): url = reverse('edit', args=(edit_id,)) 模板 {% url 'edit' edit_id %}
3)路由分发
django每一个app下面都可以有自己的urls.py路由层,templates文件夹,static静态文件夹
项目名下urls.py(总路由)不再做路由与视图函数的匹配工作,而是做一个中转
项目名下urls.py:路由分发千万不要$结尾
from django.conf.urls import include from app01 import urls as app01_urls from app02 import urls as app02_urls
url(r'^app01/', include(app01_urls)),
url(r'^app02/', include(app02_urls)),
url(r'^app03/', include('app03.urls'))
在应用下新建urls.py文件,在该文件内写路由与视图函数的对应关系即可
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/', views.index) ]
4)名称空间
当多个app中给路由与视图起别名重名的情况,使用名称空间避免反向解析不准的情况
不使用名称空间,可以在起别名的时候加上一些具有唯一标识的前缀
项目urls.py url(r'^app01/',include(app01_urls,namespace='app01')), url(r'^app02/',include(app02_urls,namespace='app02')) app01.urls.py from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/',views.index,name='index') ] app02.urls.py from django.conf.urls import url from app02 import views urlpatterns = [ url(r'^index/',views.index,name='index') ] app01.views.py reverse('app01:index') app02.views.py reverse('app02:index')
5)伪静态网页
搜索优化seo:Search Engine Optimization,利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名
url(r'^index.html',views.index,name='app01_index')
url(r'^index/\d+.html',views.index,name='app01_index')
6)虚拟环境
每一个项目都有属于自己的python环境,避免导入多余的模块造成资源的浪费;
不同的项目配置不同的python解释器,配置需要用到的模块、版本
7)django1.0与django2.0
区别1:django2.0里面的path第一个参数不支持正则,你写什么就匹配,100%精准匹配
区别2:django2.0里面的re_path对应着django1.0里面的url
虽然django2.0里面的path不支持正则表达式,但是它提供五个默认的转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
自定义转换器:
1.正则表达式
2.类 :写两个方法 to_python & to_url
3.注册:起一个名字
url(r'^index/<str:id>',views.index)
url(r'^index/<xxx:id>',views.index)
from django.urls import register_converter
# 自定义转换器
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value # 占四位,不够用0填满,超了则就按超了的位数来!
register_converter(FourDigitYearConverter, 'yyyy')
视图层
1. FBV与CBV
后端业务逻辑FBV与CBV完成
FBV:function base views,在视图里使用函数处理请求
CBV:class base views,在视图里使用类处理请求
无论是FBV还是CBV,路由层都是路由对应视图函数内存地址
# CBV 路由层
url(r'^login/', views.MyCls.as_view()) # .的左边容器类型 # CBV 视图层
from django.views import View
from django.shortcuts import HttpResponse, render
class MyCls(View): def get(self, request): return render(request, 'index.html') def post(self, request): return HttpResponse('post')
2. JsonResponse对象
是HttpRespon的子类,第一个参数是一个字典类型,
返回json的字符串,
json_dumps_params参数是一个字典,它将调用json.dumps()方法,'ensure_ascii'值为False时,传入的中文不会被编码为十六进制码
from django.http import JsonResponse
import json
json.dumps(dict, ensure_ascii=False) def index9(request): dic = {'name': 'Tom的哥哥', 'age': 18} return JsonResponse(dic, json_dumps_params={'ensure_ascii': False}) 输出: {"name": "Tom的哥哥", "age": 18}
3. 文件上传
前端需注意:
1. method 指定成 post(默认get)
2. enctype 改为 formdata格式(默认urlencoded)
后端注意点:
1. 配置文件settings.py中,注释掉csrfmiddleware中间键
2. 通过request.FILES 获取上传的post文件数据
file_obj = request.FILES.get('my_file') with open(file_obj.name, 'wb') as f: for line in file_obj.chunks(): f.write(line)