django框架之视图系统和路由系统
Posted yb635238477
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django框架之视图系统和路由系统相关的知识,希望对你有一定的参考价值。
内容回顾:
1. tags
1. for循环
{% for name in name_list %}
{{ name }}
{% endfor %}
{% for name in name_list %}
{{ name }}
{% empty %}
空空如也
{% endfor %}
forloop { }
forloop.counter 当前循环的索引值 从1开始
forloop.counter0 当前循环的索引值 从0开始
forloop.revcounter 当前循环的索引值(倒序) 到1结束
forloop.revcounter0 当前循环的索引值(倒序) 到0结束
forloop.first 单前循环是否是第一次循环 布尔值
forloop.last 单前循环是否是最后一次循环 布尔值
forloop.parentloop 当前循环的外层循环
上台阶,可以一次上1个台阶,可以上2个台阶,可以上3个台阶。问有n个台阶,有多少种走法?
2. if判断
{% if 条件 %}
操作
{% endif %}
{% if 条件 %}
操作
{% else %}
其他操作
{% endif %}
{% if 条件 %}
操作
{% elif 条件 %}
不同操作
{% else %}
其他操作
{% endif %}
注意事项:
1. 不能连续判断 a > b > C 用and连接
2. 不支持算数运算 +-*/ 用filter
3. csrf_tokrn
使用:在form表单中使用
效果:添加了一个隐藏的input标签,标签名叫csrfmiddlewaretoken 值:随机字符串
作用:提交POST请求
4. 注释 {# 注释部分 #}
2. 母板和继承
1.为什么要用母板和继承:
很多页面有重复或相似的代码,减少代码的重复和修改方便才用母板和继承
2.具体步骤:
1. 创建一个母板,‘base.html‘ 将多个页面的重复代码提取出来
2. 在母板中定义多个block,来区分不同页面的不同内容
3. 在子页面中继承母板 {% extends ‘base.html‘ %}
4. 在block中写自己页面独特的内容
3. 注意事项
1. {% extends ‘base.html‘ %} 写在第一个行
2. {% extends ‘base.html‘ %} base.html加上引号 不然当做是变量
3. 通常定义多个block,还有一个page-css 和 page-js
3. 组件
将一小部分的HTML代码写在一个模板中。———》 组件
在其他的页面中 {% include ‘nav.html‘ %}
4. 静态文件的内容
{% load static %}
"{% static ‘css/mycss.css‘ %}" ——》 /static/css/mycss.css
{% get_static_prefix %} —— 》 /static/
"{% get_static_prefix %}css/mycss.css"
5. 自定义simple_tag 和 inclusion_tag
今日内容:
视图系统
视图:
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
简单的说我们通常在views写的就是视图。只不过我们写的是基于函数的视图,即FBV。把视图写成基于类的,就是CBV啦。
这是FBV
def add_publisher(request): new_name = ‘‘ err_msg = ‘‘ if request.method == ‘POST‘: new_name = request.POST.get(‘publisher_name‘) if new_name: publisher_obj_list = models.Publisher.objects.filter(name = new_name) if not publisher_obj_list: models.Publisher.objects.create(name = new_name) return redirect(‘/publisher/‘) else: err_msg = ‘数据已存在‘ else: err_msg = ‘数据不能为空‘ return render(request, ‘add_publisher.html‘, {‘old_name‘: new_name, ‘err_msg‘: err_msg})
改成这样就是CBV啦:
from django.views import View class Addpublisher(View): def get(self,request): return render(request, ‘add_publisher.html‘) def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘数据已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
往深入的讲,他其实是实现了View中的dispatch方法,所以这个dispatch我们可以自己定义:让他去执行父类的dispatch方法:
from django.views import View class Addpublisher(View): def dispatch(self, request, *args, **kwargs):
print(‘处理请求之前‘) ret = super().dispatch(self, request, *args, **kwargs) print(‘处理请求之后‘)
def get(self,request):
print(‘这是get请求‘) return render(request, ‘add_publisher.html‘) def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘数据已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
其实这个dispatch相当于装饰器的作用。
简版的流程:
AddPublisher.as_view() ——》 view 函数
当请求来的时候才执行view
view中执行:
1. 先实例化AddPublisher,给self
2. 执行self.dispatch()
self 有dispatch 就执行自己的
没有就是执行 父类(View)的dispatch方法
3. dispatch中执行:
先通过反射获取到AddPublisher中定义get或者post方法。
执行get或者post方法 返回httpresponse对象
4. view接收到dispatch的返回值——httpresponse对象
5. view返回httpresponse对象
接下来我们给视图加上装饰器:
装饰器的作用是在函数之前或者之后做一些操作,并且不改变函数的调用方式。
def wrapper(func): def inner(*args,**kwargs): now = time.time() ret = func(*args,**kwargs) print(‘函数执行的时间是{}‘.format(time.time()-now)) return ret return inner #增加出版社 @wrapper def add_publisher(request): new_name = ‘‘ err_msg = ‘‘ if request.method == ‘POST‘: new_name = request.POST.get(‘publisher_name‘) if new_name: publisher_obj_list = models.Publisher.objects.filter(name = new_name) if not publisher_obj_list: models.Publisher.objects.create(name = new_name) return redirect(‘/publisher/‘) else: err_msg = ‘数据已存在‘ else: err_msg = ‘数据不能为空‘ return render(request, ‘add_publisher.html‘, {‘old_name‘: new_name, ‘err_msg‘: err_msg})
接下来我们给CBV加上装饰器:
from django.views import View from django.utils.decorators import method_decorator class Addpublisher(View): # def dispatch(self, request, *args, **kwargs): # return super().dispatch(self, request, *args, **kwargs) @method_decorator(wrapper) #为get请求加上装饰器 def get(self,request): return render(request, ‘add_publisher.html‘)
def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘数据已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
给dispatch方法加上装饰器,get和post请求就都加上装饰器了。
如果要在类上加装饰器,就要这样写@method_decorator(wrapper,name=‘get‘)
@method_decorator(wrapper,name=‘post‘)
request对象:
request属性:
request.method 请求方式 GTE POST
request.GET ——》 字典 URL上传的参数
request.POST ——》 字典 form表单传的参数
request.FILES 上传的文件
request.path_info 返回用户访问url,不包括域名
request.body 请求体,byte类型 request.POST的数据就是从body里面提取到的(gest请求没有请求体)
我们来写一个上传的实例
views中的代码:
def upload(request): if request.method == ‘POST‘: print(request.FILES) upload_obj = request.FILES[‘file_name‘] with open(upload_obj.name,‘wb‘)as f: for chunk in upload_obj.chunks(): f.write(chunk) return render(request,‘upload.html‘)
html的代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action=""method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file_name" > <button>提交</button> </form> </body> </html>
request方法:
request.path_info url路径不包含url参数和域名
request.get_full_path() url路径包含url参数
request.get_host() 获取ip和端口
Response对象
response方法:
1. HttpResponse 类 字符串
2. render 返回一个HTML页面
3. redirect 跳转 重定向 Location:/index/
response属性:
HttpResponse.content:响应内容
HttpResponse.charset:响应内容的编码
HttpResponse.status_code:响应的状态码
JsonResponse对象:
我们先写一个json数据:
def json_data(request): import json data = {‘name‘:‘alex‘,‘age‘:73} return HttpResponse(json.dumps(data))
然后我们在试一试django为我们提供的方法:
from django.http import JsonResponse def json_data(request): # import json data = {‘name‘:‘alex‘,‘age‘:73} # return HttpResponse(json.dumps(data)) return JsonResponse(data)
我们来说一说这两者的区别:
如果浏览器拿到的是JsonResponse的格式会自动帮你解析。而且JsonResponse只会返回字典,如果要返回列表,JsonResponse(data,safe=False)
路由系统:
基本格式:
urlpatterns = [
url(正则表达式, views视图,参数,别名),]
urlpatterns = [ url(r‘^articles/2003/$‘, views.special_case_2003), url(r‘^articles/([0-9]{4})/$‘, views.book), url(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.book), url(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.book),
]
我们匹配的时候加上小括号就是分组了,他就会将小括号里的内容以参数的形式传给views中的函数,而views中的
函数就需要参数来接受。
def book(request,*args): print(args) return HttpResponse(‘o98k‘)
我们把这样的匹配叫做无名分组
注意事项
- urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
- 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
- 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
- 每个正则表达式前面的‘r‘ 是可选的但是建议加上。
分组命名匹配:
在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
我们也可以在分组的时候加上名字,比如:
url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book),
这样views中的book函数在接受的时候就需要一个同名的参数来接受不然就会报错。
def book(request,year): print(year) return HttpResponse(‘o98k‘)
这就是命名分组!
一个公司会有不同的业务,也会有不同的app,这样我们在写url的时候就可能会有重复。
我们在项目名下的urls.py文件里可以这样写:
from django.conf.urls import url,include from app01 import urls urlpatterns = [ url(r‘app01/‘,include(urls)) ]
剩下的就交给app01来做了。
我们现在app01建一个urls.py文件,在文件中这样写:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
然后再views的文件中写book函数:
def book(request,year): print(year) return HttpResponse(‘o98k‘)
同样我们也可以有多个app文件。
项目名的urls.py文件下这样写:
from django.conf.urls import url,include from app01 import urls as app01_urls from app02 import urls as app02_urls urlpatterns = [ url(r‘app01/‘,include(app01_urls)), url(r‘app02/‘,include(app02_urls)) ]
在app01创建一个urls的py文件写:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
在app02创建一个urls的py文件写:
from django.conf.urls import url from app02 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
然后分别写好app01和app02下的book函数。
命名URL和URL反向解析:
我们在 url后面加上一个名字,name属性,这样:
url(r‘^json_data/‘,views.json_data,name=‘data‘),
就可以在views的函数中进行向解析 print(reverse(‘data‘))
具体代码就是这样的:
url(r‘^json_data/‘,views.json_data,name=‘josn_data‘),
在views函数中:
def json_data(request): print(reverse(‘ison_data‘)) data = {‘name‘:‘alex‘,‘age‘:73} return JsonResponse(data)
打印出来的结果就是:/json_data/。
在模板里面可以这样引用:
{% url ‘jaon_data‘ %}
说的简单点就是在url起一个名字,然后reverse通过这个名字反向解析拿到url地址。
参数是这样传的:
无名传参:
url(r‘^book/([0-9]{4})/[0-9]{2}‘,views.book,name=‘book‘)
函数中的参数:
reverse(‘book‘,args=(‘1995‘))
模板中的参数:
{%url ‘book‘ ‘1995‘%}
命名传参:
url(r‘^book/(?<Pmouth>[0-9]{4})/[0-9]{2}‘,views.book,name=‘book‘)
函数中的参数:
reverse(‘book‘,kwargs={‘mouth‘:‘1995‘})
模板中的参数:
{%url ‘book‘ mouth=‘1995‘%}
namespace:
如果说qpp01下和app02下有相同name的url,那么在视图中反向解析的时候找的会是谁的url呢?这就需要在项目名下的include里加一个参数
namespace=‘app01‘,namespace=‘app02‘,然后再视图中reverse(‘app01:index‘)
reverse(‘app02:index‘)
在模板中也是一样{%url ‘app01:index‘%}
举个例子:
project中的urls.py
from django.conf.urls import url, include
urlpatterns = [
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
app_name = ‘app01‘
urlpatterns = [
url(r‘^(?P<pk>d+)/$‘, views.detail, name=‘detail‘)
]
app02中的urls.py
from django.conf.urls import url
from app02 import views
app_name = ‘app02‘
urlpatterns = [
url(r‘^(?P<pk>d+)/$‘, views.detail, name=‘detail‘)
]
现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。
语法:
‘命名空间名称:URL名称‘
模板中使用:
{% url ‘app01:detail‘ pk=12 %}
views中的函数中使用
v = reverse(‘app01:detail‘, kwargs={‘pk‘:11})
这样即使app中URL的命名相同,我也可以反转得到正确的URL了。
以上是关于django框架之视图系统和路由系统的主要内容,如果未能解决你的问题,请参考以下文章