常用的请求method:
- GET请求:GET请求一般用来向服务器索取数据,但不会向服务器提交数据,不会对服务器 的状态进行更改。比如向服务器获取某篇文章的详情。
- POST请求:POST请求一般是用来向服务器提交数据,会对服务器的状态进行更改。比如提 交一篇文章给服务器。
限制请求装饰器: Django 内置的视图装饰器可以给视图提供一些限制。比如这个视图只能通过 GET 的 method 访问等。
以下将介绍一些常用的内置视图装饰器
- django.http.decorators.http.require_http_methods :这个装饰器需要传递一个允许访问的方法的列表。比如只能通过 GET 的方式访问。那么示例代码如下:
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET"])
def my_view(request):
pass
- django.views.decorators.http.require_GET :这个装饰器相当于 是 require_http_methods([‘GET’]) 的简写形式,只允许使用 GET 的 method 来访问视图。示例代码如下
from django.views.decorators.http import require_GET
@require_GET
def my_view(request):
pass
- django.views.decorators.http.require_POST :这个装饰器相当于 是 require_http_methods([‘POST’]) 的简写形式,只允许使用 POST 的 method 来访问视图。 示例代码如下
from django.views.decorators.http import require_POST
@require_POST
def my_view(request):
pass
- django.views.decorators.http.require_safe :这个装饰器相当于是 require_http_methods([‘GET’,‘HEAD’]) 的简写形式
只允许使用相对安全的方式来访问视 图。因为 GET 和 HEAD 不会对服务器产生增删改的行为。因此是一种相对安全的请求方式。 示例代码如下
from django.views.decorators.http import require_safe
@require_safe
def my_view(request):
pass
views.py
from django.shortcuts import render
from .models import Article
from django.views.decorators.http import require_http_methods,require_GET,require_POST,require_safe
from django.http import HttpResponse
# require_GET = @require_http_methods(['GET'])
# require_safe = @require_http_methods(['GET','HEAD'])
@require_GET
def index(request):
# 首页返回所有的文章
# 只能使用GET请求来访问这个视图函数
articles = Article.objects.all()
return render(request,'index.html',context={"articles":articles})
@require_http_methods(['POST','GET'])
def add_article(request):
# 如果使用GET请求来访问这个视图函数,那么就返回一个添加文章的HTML页面
# 如果使用POST请求来访问这个视图函数,那么就获取提交上来的数据,然后保存
# 到数据库中
if request.method == 'GET':
return render(request,'add_article.html')
else:
title = request.POST.get("title")
content = request.POST.get('content')
Article.objects.create(title=title,content=content)
return HttpResponse('success')
add_article.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<table>
<tbody>
<tr>
<td>标题:</td>
<td><input type="text" name="title"></td>
</tr>
<tr>
<td>内容:</td>
<td>
<textarea name="content" id="" cols="30" rows="10"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="提交"></td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转 到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应 该给他重定向到登录页面。
永久性重定向:http的状态码是301,多用于旧网址被废弃了要转到一个新的网址确保用户的
访问,最经典的就是京东网站,你输入www.jingdong.com的时候,会被重定向到
www.jd.com,因为jingdong.com这个网址已经被废弃了,被改成jd.com,所以这种情况下应 该用永久重定向。
暂时性重定向:http的状态码是302,表示页面的暂时性跳转。比如访问一个需要权限的网
址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
在 Django 中,重定向是使用
redirect(to, *args, permanent=False, **kwargs)
来实 的。
to 是一个 url , permanent 代表的是这个重定向是否是一个永久的重定向,默认 是 False 。关于重定向的使用。请看以下例子:
from django.shortcuts import reverse,redirect
def profile(request):
if request.GET.get("username"):
return HttpResponse("%s,欢迎来到个人中心页面!")
else:
return redirect(reverse("user:login"))
views.py
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
def index(request):
# 如果没有登录,那么就重定向到注册页面
# 如果在url中,传递了usename这个参数,那么就认为是登录了,否则就没有登录
# /?username=xxx
username = request.GET.get("username")
if username:
return HttpResponse("首页")
else:
return redirect(reverse('signup'))
# return HttpResponse("注册页")
def signup(request):
return HttpResponse("注册页")
Django在接收到http请求之后,会根据http请求携带的参数以及报文信息创建一个 WSGIRequest 对 象,并且作为视图函数第一个参数传给视图函数。也就是我们经常看到的 request 参数。在这个对象上我们可以找到客户端上传上来的所有信息。这个对象的完整路径是django.core.handlers.wsgi.WSGIRequest 。
WSGIRequest对象常用属性: WSGIRequest 对象上大部分的属性都是只读的。因为这些属性是从客户端上传上来的,没必要做任何的修改。以下将对一些常用的属性进行讲解:
- path :请求服务器的完整“路径”,但不包含域名和参数。比 如
http://www.baidu.com/xxx/yyy/ ,那么 path 就是 /xxx/yyy/ 。- method:代表当前请求的 http 方法。比如是 GET 还是 POST 。
- GET :一个django.http.request.QueryDict 对象。操作起来类似于字典。这个属性中包含了 所有以 ?xxx=xxx的方式上传上来的参数。
- POST :也是一个 django.http.request.QueryDict 对象。这个属性中包含了所有以
POST 方 式上传上来的参数。- FILES :也是一个 django.http.request.QueryDict对象。这个属性中包含了所有上传的文 件。
- COOKIES :一个标准的Python字典,包含所有的 cookie,键值对都是字符串类型。
- session :一个类似于字典的对象。用来操作服务器的 session 。
- META:存储的客户端发送上来的所有 header 信息。
- CONTENT_LENGTH :请求的正文的长度(是一个字符串)。
- CONTENT_TYPE :请求的正文的MIME类型。
- HTTP_ACCEPT :响应可接收的Content-Type。
- HTTP_ACCEPT_ENCODING :响应可接收的编码。
- HTTP_ACCEPT_LANGUAGE : 响应可接收的语言。
- HTTP_HOST :客户端发送的HOST值。
- HTTP_REFERER :在访问这个页面上一个页面的url。
- QUERY_STRING :单个字符串形式的查询字符串(未解析过的形式)。
- REMOTE_ADDR:客户端的IP地址。如果服务器使用了 nginx 做反向代理或者负载均衡,那么 这个值返回的是 127.0.0.1 ,这时候可以使用
HTTP_X_FORWARDED_FOR 来获取,所以获 取 ip 地址的代码片段如下: if
request.META.has_key(‘HTTP_X_FORWARDED_FOR’): ip =
request.META[‘HTTP_X_FORWARDED_FOR’] 94 else:ip =
request.META[‘REMOTE_ADDR’]- REMOTE_HOST :客户端的主机名。
- REQUEST_METHOD :请求方法。一个字符串类似于 GET 或者 POST 。
- SERVER_NAME :服务器域名。
- SERVER_PORT :服务器端口号,是一个字符串类型。
WSGIRequest对象常用方法:
- is_secure() :是否是采用 https 协议。
- is_ajax() :是否采用ajax 发送的请求。原理就是判断请求头中是否存在 X-Requested- With:XMLHttpRequest 。
- get_host() :服务器的域名。如果在访问的时候还有端口号,那么会加上端口号。比 如 www.baidu.com:9000 。
- get_full_path() :返回完整的path。如果有查询字符串,还会加上查询字符串。比 如
/music/bands/?print=True 。- get_raw_uri() :获取请求的完整 url 。
from django.shortcuts import render
from django.http import HttpResponse
from django.core.handlers.wsgi import WSGIRequest
def index(request):
# print(type(request))
print(request.path)
return HttpResponse('index')
def login(request):
# print(request.path)
# print(request.get_full_path())
# /login/?username=xxx&password=111111
# print(request.get_raw_uri())
# for key,value in request.META.items():
# print("%s:%s"%(key,value))
# print(request.get_host())
# print(request.is_secure())
print(request.is_ajax())
return HttpResponse("login")
我们平时用的 request.GET 和 request.POST 都是 QueryDict 对象,这个对象继承自 dict ,因此 用法跟dict 相差无几。其中用得比较多的是 get 方法和 getlist 方法。
- get 方法:用来获取指定 key 的值,如果没有这个 key ,那么会返回 None 。
- getlist 方法:如果浏览器上传上来的 key 对应的值有多个,那么就需要通过这个方法获 取
from django.shortcuts import render
from django.http import HttpResponse
from django.http.request import QueryDict
from django.views.decorators.http import require_http_methods
def index(request):
# username = request.GET['username']
username = request.GET.get('p',default=1)
print(username)
return HttpResponse('success')
@require_http_methods(['GET','POST'])
def add_article(request):
if request.method == 'GET':
return render(request,'add_article.html')
else:
title = request.POST.get("title")
content = request.POST.get('content')
tags = request.POST.getlist('tags') # getlist获取所有的值
print('title:',title)
print('content:',content)
print('tags:',tags)
return HttpResponse("success")
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<table>
<tbody>
<tr>
<td>标题:</td>
<td>
<input type="text" name="title">
</td>
</tr>
<tr>
<td>内容:</td>
<td>
<input type="text" name="content">
</td>
</tr>
<tr>
<td>标签:</td>
<td>
<label>
Python
<input type="checkbox" name="tags" value="python">
</label>
<label>
Django
<input type="checkbox" name="tags" value="django">
</label>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="提交">
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
HttpResponse对象 Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一 个 HttpRequest对象传给视图函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响应 给浏览器。而这个响应,我们必须返回 HttpResponseBase或者他的子类的对象。 而 HttpResponse 则是 HttpResponseBase 用得最多的子类。那么接下来就来介绍一 下HttpResponse 及其子类。
常用属性:
- content:返回的内容。
- status_code:返回的HTTP响应状态码。
- content_type:返回的数据的MIME类型,默认为 text/html 。浏览器会根据这个属性,来显 示数据。如果是 text/html ,那么就会解析这个字符串,如果 text/plain ,那么就会显示一 个纯文本。常用的 Content-Type
如下:
text/html(默认的,html文件)
text/plain(纯文本)
text/css(css文件)
text/javascript(js文件)
multipart/form-data(文件提交)
application/json(json传输)
application/xml(xml文件)- 设置请求头: response[‘X-Access-Token’] = ‘xxxx’ 。
常用方法:
- set_cookie:用来设置 cookie 信息。后面讲到授权的时候会着重讲到。
- delete_cookie:用来删除 cookie 信息。
- write: HttpResponse 是一个类似于文件的对象,可以用来写入数据到数据体(content)中。
#encoding: utf-8
from django.http import HttpResponse,JsonResponse
import json
def index(request):
response = HttpResponse('Smj
',content_type='text/plain;charset=utf-8')
# response.status_code = 400
response['X-Token'] = 'z'
# response.content = 'Smj'
response.write('z')
return response
def jsonresponse_view(request):
persons = [
{
'username': 'z',
'age': 18,
'height': 180
},
{
'username': 'z1',
'age': 20,
'height': 180
}
]
# person_str = json.dumps(person)
# response = HttpResponse(person_str,content_type='application/json')
# return response
response = JsonResponse(persons,safe=False)
return response
用来对象 dump 成 json 字符串,然后返回将 json 字符串封装成 Response 对象返回给浏览器。 并且他的Content-Type 是 application/json 。示例代码如下
from django.http import JsonResponse
def index(request):
return JsonResponse({"username":"zhiliao","age":18})
默认情况下 JsonResponse 只能对字典进行 dump ,如果想要对非字典的数据进行 dump ,那么需 要给 JsonResponse 传递一个 safe=False 参数。示例代码如下:
from django.http import JsonResponse
def index(request): persons = ['张三','李四','王五']
return HttpResponse(persons)
以上代码会报错,应该在使用 HttpResponse 的时候,传入一个 safe=False 参数,示例代码如 下:
return HttpResponse(persons,safe=False)
#encoding: utf-8
from django.http import HttpResponse,JsonResponse
import json
def index(request):
response = HttpResponse('Smj
',content_type='text/plain;charset=utf-8')
# response.status_code = 400
response['X-Token'] = 'z'
# response.content = 'Smj'
response.write('z')
return response
def jsonresponse_view(request):
persons = [
{
'username': 'z',
'age': 18,
'height': 180
},
{
'username': 'z1',
'age': 20,
'height': 180
}
]
# person_str = json.dumps(person)
# response = HttpResponse(person_str,content_type='application/json')
# return response
response = JsonResponse(persons,safe=False)
return response
生成CSV文件: 有时候我们做的网站,需要将一些数据,生成有一个 CSV 文件给浏览器,并且是作为附件的形式 下载下来。以下将讲解如何生成CSV 文件。
生成小的CSV文件: 这里将用一个生成小的 CSV 文件为例,来把生成 CSV 文件的技术要点讲到位。我们用 Python 内置的csv模块来处理 csv 文件,并且使用 HttpResponse 来将 csv 文件返回回去。示例代码如 下:
import csv
from django.http import HttpResponse
def csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['username', 'age', 'height', 'weight'])
writer.writerow(['zhiliao', '18', '180', '110'])
return response
这里再来对每个部分的代码进行解释:
- 我们在初始化 HttpResponse 的时候,指定了 Content-Type 为 text/csv ,这将告诉浏览器, 这是一个 csv 格式的文件而不是一个 HTML 格式的文件,如果用默认值,默认值就是 html , 那么浏览器将把 csv 格式的文件按照 html格式输出,这肯定不是我们想要的。
- 第二个我们还在 response 中添加一个 Content-Disposition 头,这个东西是用来告诉浏览器 该如何处理这个文件,我们给这个头的值设置为 attachment; ,那么浏览器将不会对这个文 件进行显示,而是作为附件的形式下载,第二个 filename=“somefilename.csv” 是用来指定这 个 csv 文件的名字。
- 我们使用 csv 模块的 writer 方法,将相应的数据写入到 response 中。
#encoding: utf-8
from django.http import HttpResponse,StreamingHttpResponse
from django.template import loader
import csv
def index(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = "attachment;filename='abc.csv'"
# with open('xx.csv','w') as fp:
# csv.writer(fp)
writer = csv.writer(response)
writer.writerow(['username','age'])
writer.writerow(['z',18])
return response
def template_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = "attachment;filename='abc.csv'"
context = {
'rows': [
['username','age'],
['z',18],
]
}
template = loader.get_template('abc.txt')
csv_template = template.render(context)
response.content = csv_template
return response
class Echo:
def write(self,value):
return value
def large_csv_view(request):
response = StreamingHttpResponse(content_type='text/csv')
response['Content-Disposition'] = "attachment;filename='large.csv'"
rows = ("Row {},{}\n".format(row,row) for row in range(0,1000000))
response.streaming_content = rows
return response
# response = HttpResponse(content_type='text/csv')
# response['Content-Disposition'] = "attachment;filename='large.csv'"
# writer = csv.writer(response)
# for row in range(0,1000000):
# writer.writerow(['Row {}'.format(row),'{}'.format(row)])
# return response
以上的例子是生成的一个小的 csv 文件,如果想要生成大型的 csv 文件,那么以上方式将有可能 会发生超时的情况(服务器要生成一个大型csv文件,需要的时间可能会超过浏览器默认的超时时 间)。这时候我们可以借助另外一个类,叫做
StreamingHttpResponse 对象,这个对象是将响应的 数据作为一个流返回给客户端,而不是作为一个整体返回。示例代码如下:
class Echo:
""" 定义一个可以执行写操作的类,以后调用csv.writer的时候,就会执行这个方法 """
def write(self, value):
return value
def large_csv(request):
rows = (["Row {}".format(idx), str(idx)] for idx in range(655360))
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in rows),content_typ e="text/csv")
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response
这里我们构建了一个非常大的数据集 rows ,并且将其变成一个迭代器
然后因为 StreamingHttpResponse 的第一个参数只能是一个生成器,因此我们使用圆括 号 (writer.writerow(row) for row in rows)
并且因为我们要写的文件是 csv 格式的文件
因此需要调用 writer.writerow 将 row 变成一个 csv 格式的字符串
而调用 writer.writerow 又需要一个中间的容器
因此这里我们定义了一个非常简单的类 Echo
这个类只实现一个 write 方法
以后在执行 csv.writer(pseudo_buffer) 的时候
就会调用 Echo.writer 方法。
注意: StreamingHttpResponse 会启动一个进程来和客户端保持长连接,所以会很消耗资源。所以 如果不是特殊要求,尽量少用这种方法。
关于StreamingHttpResponse: 这个类是专门用来处理流数据的。使得在处理一些大型文件的时候,不会因为服务器处理时间过长而到时连接超时。这个类不是继承自 HttpResponse ,并且跟 HttpResponse 对比有以下几点区别:
- 这个类没有属性 content ,相反是 streaming_content 。
- 这个类的 streaming_content 必须是一个可以迭代的对象。
- 这个类没有 write 方法,如果给这个类的对象写入数据将会报错。 注意: StreamingHttpResponse 会启动一个进程来和客户端保持长连接,所以会很消耗资源。所以 如果不是特殊要求,尽量少用这种方法。
在写视图的时候, Django 除了使用函数作为视图,也可以使用类作为视图。使用类视图可以使用 类的一些特性,比如继承等。
View:
django.views.generic.base.View是主要的类视图,所有的类视图都是继承自他。如果我们写自 己的类视图,也可以继承自他。然后再根据当前请求的 method 来实现不同的方法。比如这个视图只能使用 get 的方式来请求,那么就可以在这个类中定义 get(self,request,*args,**kwargs) 方法。以此类推,如果只需要实现 post 方法,那么就只需 要在类中实现 post(self,request,*args,**kwargs) 。示例代码如下
from django.views import View
class BookDetailView(View):
def get(self,request,*args,**kwargs):
return render(request,'detail.html')
类视图写完后,还应该在 urls.py 中进行映射,映射的时候就需要调用 View 的类方 法 as_view() 来进行转换。示例代码如下:
urlpatterns = [ path("detail//" ,views.BookDetailView.as_view(),name='detail') ]
除了 get 方法, View 还支持以下方法
[‘get’,‘post’,‘put’,‘patch’,‘delete’,‘head’,‘options’,‘trace’]
如果用户访问了 View 中没有定义的方法。比如你的类视图只支持 get 方法,而出现了 post 方 法,那么就会把这个请求转发给 http_method_not_allowed(request,*args,**kwargs) 。示例代码如下
class AddBookView(View):
def post(self,request,*args,**kwargs):
return HttpResponse("书籍添加成功!")
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("您当前采用的method是:%s,本视图只支持使用post请求!" % reque st.method)
urls.py 中的映射如下:
path("addbook/",views.AddBookView.as_view(),name='add_book')
如果你在浏览器中访问 addbook/ ,因为浏览器访问采用的是 get 方法,而 addbook 只支 持 post 方法,因此以上视图会返回您当前采用的 method 是: GET ,本视图只支持使用 post 请 求!。 其实不管是 get 请求还是 post 请求,都会dispatch(request,*args,**kwargs) 方法,所以如 果实现这个方法,将能够对所有请求都处理到。
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
from .models import Article
from django.views.generic import ListView,View
from django.core.paginator import Paginator,Page
from django.utils.decorators import method_decorator
def add_article(request):
articles = []
for x in range(0,102):
article = Article(title='标题:%s'%x,content='内容:%s'%x)
articles.append(article)
Article.objects.bulk_create(articles)
return HttpResponse('article added successfully')
class ArticleListView(ListView):
model = Article
template_name = 'article_list1.html'
context_object_name = 'articles'
paginate_by = 10
ordering = 'create_time'
page_kwarg = 'p'
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(*kwargs)
context['username'] = 'zhiliao'
paginator = context.get('paginator')
page_obj = context.get('page_obj')
pagination_data = self.get_pagination_data(paginator,page_obj,3)
context.update(pagination_data)
return context
def get_pagination_data(self,paginator,page_obj,around_count=2):
current_page = page_obj.number
num_pages = paginator.num_pages
left_has_more = False
right_has_more = False
if current_page <= around_count + 2:
left_pages = range(1,current_page)
else:
left_has_more = True
left_pages = range(current_page-around_count,current_page)
if current_page >= num_pages - around_count - 1:
right_pages = range(current_page+1,num_pages+1)
else:
right_has_more = True
right_pages = range(current_page+1,current_page+around_count+1)
return {
'left_pages': left_pages,
'right_pages': right_pages,
'current_page': current_page,
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'num_pages': num_pages
}
# def get_queryset(self):
# return Article.objects.filter(id__lte=9)
def login_required(func):
def wrapper(request,*args,**kwargs):
# ?username=zhiliao
username = request.GET.get('username')
if username:
return func(request,*args,**kwargs)
else:
return redirect(reverse('front:login'))
return wrapper
@method_decorator([login_required],name='dispatch')
class ProfileView(View):
def get(self,request):
return HttpResponse("个人中心界面")
# @method_decorator(login_required)
# def dispatch(self, request, *args, **kwargs):
# return super(ProfileView, self).dispatch(request,*args,**kwargs)
def login(request):
return HttpResponse('login')
TemplateView: django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。在这个类中, 有两个属性是经常需要用到的
一个是 template_name ,这个属性是用来存储模版的路 径, TemplateView 会自动的渲染这个变量指向的模版。
另外一个是 get_context_data ,这个方 法是用来返回上下文数据的,也就是在给模版传的参数的。示例代码如下:
from django.views.generic.base
import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['username'] = "黄"
return context
在 urls.py 中的映射代码如下:
from django.urls import path
from myapp.views import HomePageView
urlpatterns = [ path('', HomePageView.as_view(), name='home'), ]
如果在模版中不需要传递任何参数,那么可以直接只在 urls.py 中使用 TemplateView 来渲染模 版。示例代码如下:
from django.urls import path
from django.views.generic import
TemplateView urlpatterns =
[
path('about/', TemplateView.as_view(template_name="about.html")),
]
from django.urls import path,include
from . import views
from django.views.generic import TemplateView
urlpatterns = [
path('', views.index,name='index'),
path('book/', views.BookListView.as_view(),name='book_list'),
path('add_book/', views.AddBookView.as_view(),name='add_book'),
path('detail//' ,views.BookDetailView.as_view(),name='detail'),
# 以后如果渲染的这个模板不需要传递任何的参数,那么建议在urls中使用TemplateView
# path('about/',TemplateView.as_view(template_name='about.html'))
path('about/',views.AboutView.as_view()),
path('article/',include('front.urls'))
]
#encoding: utf-8
from django.http import HttpResponse
from django.shortcuts import render
from django.views.generic import View,TemplateView
def index(request):
return HttpResponse('index')
class BookListView(View):
def get(self,request,*args,**kwargs):
return HttpResponse("book list view")
class AddBookView(View):
def get(self,request,*args,**kwargs):
return render(request,'add_book.html')
def post(self,request,*args,**kwargs):
book_name = request.POST.get("name")
book_author = request.POST.get("author")
print("name:{},author:{}".format(book_name,book_author))
return HttpResponse("success")
class BookDetailView(View):
def get(self,request,book_id):
print('图书的id是:%s'%book_id)
return HttpResponse("success")
def dispatch(self, request, *args, **kwargs):
print('dispatch')
return super(BookDetailView, self).dispatch(request,*args,**kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("不支持GET以外的其他请求!")
class AboutView(TemplateView):
template_name = 'about.html'
def get_context_data(self, **kwargs):
context = {"phone":'0731-888888'}
return context
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是关于我们的页面{{ phone }}
</body>
</html>
在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书 列表等等。在 Django 中可以使用 ListView 来帮我们快速实现这种需求。示例代码如下:
class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
paginate_by = 10
context_object_name = 'articles'
ordering = 'create_time'
page_kwarg = 'page'
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
print(context)
return context
def get_queryset(self):
return Article.objects.filter(id__lte=89)
对以上代码进行解释:
- 首先 ArticleListView 是继承自 ListView 。
- model :重写 model 类属性,指定这个列表是给哪个模型的。
- template_name :指定这个列表的模板。
- paginate_by :指定这个列表一页中展示多少条数据。
- context_object_name :指定这个列表模型在模板中的参数名称。
- ordering :指定这个列表的排序方式。
- page_kwarg :获取第几页的数据的参数名称。默认是 page 。
- get_context_data :获取上下文的数据。
- get_queryset :如果你提取数据的时候,并不是要把所有数据都返回,那么你可以重写这个方法。将一些不需要展示的数据给过滤掉。
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
from .models import Article
from django.views.generic import ListView,View
from django.core.paginator import Paginator,Page
from django.utils.decorators import method_decorator
def add_article(request):
articles = []
for x in range(0,102):
article = Article(title='标题:%s'%x,content='内容:%s'%x)
articles.append(article)
Article.objects.bulk_create(articles)
return HttpResponse('article added successfully')
class ArticleListView(ListView):
model = Article
template_name = 'article_list1.html'
context_object_name = 'articles'
paginate_by = 10
ordering = 'create_time'
page_kwarg = 'p'
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(*kwargs)
# 继承父类的参数,否则可能导致某些参数无法使用
context['username'] = 'zhiliao'
paginator = context.get('paginator')
page_obj = context.get('page_obj')
pagination_data = self.get_pagination_data(paginator,page_obj,3)
context.update(pagination_data)
return context
def get_pagination_data(self,paginator,page_obj,around_count=2):
current_page = page_obj.number
num_pages = paginator.num_pages
left_has_more = False
right_has_more = False
if current_page <= around_count + 2:
left_pages = range(1,current_page)
else:
left_has_more = True
left_pages = range(current_page-around_count,current_page)
if current_page >= num_pages - around_count - 1:
right_pages = range(current_page+1,num_pages+1)
else:
right_has_more = True
right_pages = range(current_page+1,current_page+around_count+1)
return {
'left_pages': left_pages,
'right_pages': right_pages,
'current_page': current_page,
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'num_pages': num_pages
}
# def get_queryset(self):
# return Article.objects.filter(id__lte=9)
def login_required(func):
def wrapper(request,*args,**kwargs):
# ?username=zhiliao
username = request.GET.get('username')
if username:
return func(request,*args,**kwargs)
else:
return redirect(reverse('front:login'))
return wrapper
@method_decorator([login_required],name='dispatch')
class ProfileView(View):
def get(self,request):
return HttpResponse("个人中心界面")
# @method_decorator(login_required)
# def dispatch(self, request, *args, **kwargs):
# return super(ProfileView, self).dispatch(request,*args,**kwargs)
def login(request):
return HttpResponse('login')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% endfor %}
<ul class="pagination">
{# 上一页#}
{% if page_obj.has_previous %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">上一页</a></li>
{% endif %}
{# 中间的页码#}
{% for page in paginator.page_range %}
{% if page == page_obj.number %}
<li class="active"><a href="javascript:void(0);">{{ page }}</a></li>
{% else %}
<li><a href="{% url 'front:article_list' %}?p={{ page }}">{{ page }}</a></li>
{% endif %}
{% endfor %}
{# 下一页#}
{% if page_obj.has_next %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">下一页</a></li>
{% endif %}
</ul>
</ul>
</body>
</html>
Paginator和Page类: Paginator 和 Page 类都是用来做分页的。
他们在 Django 中的路径为
django.core.paginator.Paginator 和 django.core.paginator.Page
以下对这两个类的常用属性和方法做解释:
Paginator常用属性和方法:
- count :总共有多少条数据。
- num_pages :总共有多少页。
- page_range :页面的区间。比如有三页,那么就 range(1,4) 。
Page常用属性和方法:
- has_next :是否还有下一页。
- has_previous :是否还有上一页。
- next_page_number :下一页的页码。
- previous_page_number :上一页的页码。
- number :当前页。
- start_index :当前这一页的第一条数据的索引值。
- end_index :当前这一页的最后一条数据的索引值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% endfor %}
<ul class="pagination">
{# 上一页#}
{% if page_obj.has_previous %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">上一页</a></li>
{% endif %}
{# 中间的页码#}
{% for page in paginator.page_range %}
{% if page == page_obj.number %}
<li class="active"><a href="javascript:void(0);">{{ page }}</a></li>
{% else %}
<li><a href="{% url 'front:article_list' %}?p={{ page }}">{{ page }}</a></li>
{% endif %}
{% endfor %}
{# 下一页#}
{% if page_obj.has_next %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">下一页</a></li>
{% endif %}
</ul>
</ul>
</body>
</html>
def get_pagination_data(self,paginator,page_obj,around_count=2):
current_page = page_obj.number
num_pages = paginator.num_pages
left_has_more = False
right_has_more = False
if current_page <= around_count + 2:
left_pages = range(1,current_page)
else:
left_has_more = True
left_pages = range(current_page-around_count,current_page)
if current_page >= num_pages - around_count - 1:
right_pages = range(current_page+1,num_pages+1)
else:
right_has_more = True
right_pages = range(current_page+1,current_page+around_count+1)
return {
'left_pages': left_pages,
'right_pages': right_pages,
'current_page': current_page,
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'num_pages': num_pages
}
def get_pagination_data(self,paginator,page_obj,around_count=2):
current_page = page_obj.number
num_pages = paginator.num_pages
left_has_more = False
right_has_more = False
if current_page <= around_count + 2:
left_pages = range(1,current_page)
else:
left_has_more = True
left_pages = range(current_page-around_count,current_page)
if current_page >= num_pages - around_count - 1:
right_pages = range(current_page+1,num_pages+1)
else:
right_has_more = True
right_pages = range(current_page+1,current_page+around_count+1)
return {
'left_pages': left_pages,
'right_pages': right_pages,
'current_page': current_page,
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'num_pages': num_pages
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% endfor %}
<ul class="pagination">
{# 上一页#}
{% if page_obj.has_previous %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">上一页</a></li>
{% endif %}
{% if left_has_more %}
<li><a href="{% url 'front:article_list' %}?p=1">1</a></li>
<li><a href="javascript:void(0);">...</a></li>
{% endif %}
{# 左边的页码 #}
{% for left_page in left_pages %}
<li><a href="{% url 'front:article_list' %}?p={{ left_page }}">{{ left_page }}</a></li>
{% endfor %}
{# 当前的页面 #}
<li class="active"><a href="{% url 'front:article_list' %}?p={{ current_page }}">{{ current_page }}</a></li>
{# 右边的页码 #}
{% for right_page in right_pages %}
<li><a href="{% url 'front:article_list' %}?p={{ right_page }}">{{ right_page }}</a></li>
{% endfor %}
{% if right_has_more %}
<li><a href="javascript:void(0);">...</a></li>
<li><a href="{% url 'front:article_list' %}?p={{ num_pages }}">{{ num_pages }}</a></li>
{% endif %}
{# 下一页#}
{% if page_obj.has_next %}
<li><a href="{% url 'front:article_list' %}?p={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">下一页</a></li>
{% endif %}
</ul>
</ul>
</body>
</html>
在开发中,有时候需要给一些视图添加装饰器。如果用函数视图那么非常简单,只要在函数的上面 写上装饰器就可以了。但是如果想要给类添加装饰器,那么可以通过以下两种方式来实现:
装饰dispatch方法:
from django.utils.decorators import method_decorator
def login_required(func):
def wrapper(request,*args,**kwargs):
if request.GET.get("username"):
return func(request,*args,**kwargs)
else:
return redirect(reverse('index'))
return wrapper
class IndexView(View):
def get(self,request,*args,**kwargs):
return HttpResponse("index")
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
super(IndexView, self).dispatch(request,*args,**kwargs)
直接装饰在整个类上:
from django.utils.decorators import method_decorator
def login_required(func):
def wrapper(request,*args,**kwargs):
if request.GET.get("username"):
return func(request,*args,**kwargs)
else:
return redirect(reverse('login'))
return wrapper
@method_decorator(login_required,name='dispatch') # name就指定了是下面的dispatch方法
class IndexView(View):
def get(self,request,*args,**kwargs):
return HttpResponse("index")
def dispatch(self, request, *args, **kwargs):
super(IndexView, self).dispatch(request,*args,**kwargs)
错误处理 在一些网站开发中。经常会需要捕获一些错误,然后将这些错误返回比较优美的界面,或者是将这个错误的请求做一些日志保存。。
常用的错误码:
404 :服务器没有指定的url。
403 :没有权限访问相关的数据。
405 :请求的 method 错误。
400 : bad request ,请求的参数错误。
500 :服务器内部错误,一般是代码出bug了。
502 :一般部署的时候见得比较多,一般是 nginx 启动了,然后 uwsgi 有问题。
自定义错误模板: 在碰到比如 404 , 500 错误的时候,想要返回自己定义的模板。那么可以直接在 templates 文件 夹下创建相应错误代码的 html 模板文件。那么以后在发生相应错误后,会将指定的模板返回回去。
错误处理的解决方案:
对于 404 和 500 这种自动抛出的错误。我们可以直接在 templates 文件夹下新建相应错误代码的 模板文件。而对于其他的错误,我们可以专门定义一个 app ,用来处理这些错误
#encoding: utf-8
from django.http import HttpResponse
from django.shortcuts import render
def view_405(request):
return render(request,'errors/405.html',status=405)
def view_403(request):
return render(request,'errors/403.html',status=403)
from django.urls import path
from . import views
app_name = 'errors'
urlpatterns = [
path('405.html',views.view_405,name='405'),
path('403.html',views.view_403,name='403'),
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
您访问的页面已经到火星上去了~~
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
服务器出现了一个小虫子,工程师们正在紧急捕捉...
</body>
</html>