视图中对于数据库的操作请参考另一篇博客,Django概述:https://blog.csdn.net/qq_27114273/article/details/92397401
request是Django框架根据Http请求报文生成的对象,包含了请求的所有信息,默认是视图函数的第一个参数
组成属性:
几个方法:
request.POST.has_key()
,返回布尔值,是否包含所给的键QueryDict对象,是一个类似字典的类,被设计成可以处理同一个键有多个值的情况。它的实例是一个不可变对象,也就是说不能改变request.POST或者request.GET的值
request请求上传的文件包含在FILES里面,键来自中的name,只在POST请求中存在,并且提交的包含entype="multipart/form-data"时才生效,否则FILES只是一个空的类字典对象。
以图像为例,展示一个文件的上传和存储的过程。首先在视图函数中接受这个文件,具体实现的思路是,将文件拆分成小块的可迭代对象,然后将其写入到文件里面。
from DjangoView.settings import MEDIA_ROOT
def upload(request):
if request.method = "POST":
username = request.POST.get("username")
icon = request.FILES.get("icon")
save_filename = os.path.join(MEDIA_ROOT, icon.name)
# 用with方法来实现文件存储
with open(save_filename, "wb") as save_file:
# chunks 将文件拆分差成块的可迭代对象
for part in icon.chunks():
save_file.write(part)
save_file.flush()
user = User(username=username, icon=save_filename)
user.save()
return HttpResponse("upload file")
通过返回方法生成的实例来设置cookie,分为加盐和不加盐,加盐的cookie更安全
resp = HttpResponse("content")
# 设置cookie
resp.set_cookie("key", "value", max_age="过期时间")
# 删除cookie
del request.COOKIES["key"] # 删除了服务器的cookie,浏览器还有
resp.delete_cookie("key") # 删除了对应键的值,键还存在
resp.flush() # 删除所有cookie
# 获取cookie
request.COOKIES.get("key")
加盐的cookie设置获取和删除
value = request.POST.get("name")
resp = HttpResponse("redirect to login")
# 设置加盐cookie,盐是一个字符串
response.set_signed_cookie("key", "value", salt="String")
# 获取加盐cookie,需要提供加的盐
value = request.get_signed_cookie("key", salt="String")
# 删除加盐cookie
resp.delete_cookie("key")
return resp
cookie的参数:
session是数据保存在服务器的回话技术,flask默认将session存在了cookie中,django默认存在了ORM中,在迁移时默认生成一张django-session
的表,django将session持久化到了内存中。
主要有三个字段:
def login(request):
username = request.POST.get("username")
# 设置session
request.session["username"] = username
# 获取session的值
username = request.session.get("username")
# cookie session 一起干掉
request.session.flush()
return HttpResponse("you are in")
你也可以在模板里面,用模板语法获取到session
<h3>{{ request.session.username }}h3>
session实现在redis中存取借助了一个模块pip install django-redis-sessions
,或者在下载页面下载然后python setup.py install
,github地址:https://github.com/martinrusev/django-redis-sessions ,安装之后需要在settings.py里面设置如下
# 引擎设置
SESSION_ENGINE = 'redis_session.session'
# 链接设置
SESSION_REDIS = {
'host': 'localhost',
'port': 6379,
'db': 0,
'password': 'password',
'prefix': 'session',
'socket_timeout': 1,
'retry_on_timeout': False
}
# 如果使用远程服务器的redis
SESSION_REDIS = {
'unix_domain_socket_path': '/var/run/redis/redis.sock',
'db': 0,
'password': 'password',
'prefix': 'session',
'socket_timeout': 1,
'retry_on_timeout': False
}
集群的redis需要配置redis哨兵(Redis Sentinel),即从服务器,也可以设置Redis Pool
# 配置哨兵信息
SESSION_REDIS_SENTINEL_LIST = [(host, port), (host, port), (host, port)]
SESSION_REDIS_SENTINEL_MASTER_ALIAS = 'sentinel-master'
# 配置redis池
SESSION_REDIS = {
'prefix': 'session',
'socket_timeout': 1
'retry_on_timeout': False,
'pool': [{
'host': 'localhost3',
'port': 6379,
'db': 0,
'password': None,
'unix_domain_socket_path': None,
'url': None,
'weight': 1
},
{
'host': 'localhost2',
'port': 6379,
'db': 0,
'password': None,
'unix_domain_socket_path': None,
'url': None,
'weight': 1
},
{
'host': 'localhost1',
'port': 6379,
'db': 0,
'password': None,
'unix_domain_socket_path': None,
'url': None,
'weight': 1
}]
}
配置好redis的连接,就可以使用redis来存session了,存取的命令没有任何的改变,Django会自动帮我们完成。
Django是自带缓存系统的,默认将缓存放在的了配置的数据库中,在终端命令行里面可以使用默认命令创建缓存表,python manager.py createcachetable TableName
,会在数据库中生成一张自定义名称TableName
的表,用来存储缓存,包含三个参数,都不允许为空
需要在settings.py中配置缓存数据库:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'TableName',
'TIMEOUT': '60',
'KEY_PREFIX': 'Prefix',
'VERSION': '1',
}
}
缓存的存取:
from django.core.cache import cache
resp = render(request, "person_list.html", locals())
# 设置缓存
cache.set("persons", resp, timeout=60*5)
return resp
# 获取缓存
result = cache.get("persons")
用redis来实现缓存是非常理想的方式,Django中配置redis作为缓存数据库,需要用到django-redis
,或者django-redis-cache
模块,配置基本一直,以django-redis为例
虚拟环境输入pip install django-redis
,然后在settings.py里面配置缓存:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# "PASSWORD": "密码",
}
}
}
缓存的存取写法不变。
我们也可以使用redis实现全站缓存,来提高服务器的运行效率。 使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,
当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
# 中间件
MIDDLEWARE = [
'django.middleware.cache.UpdataCacheMiddleware',
# 其他中间件
'django.middleware.cache.FetchFromCacheMeddileware',
]
缓存可以在单独的视图中使用
方法一:通过装饰器
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def login(request):
username = cache.get("username")
方法二:通过url
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'^login/', cache_page(60 * 15)(login)),
]
Django自带了一个分页器Paginator,帮助我们实现多条数据的展示,当我们实例化一个Paginator类的实例时,需要给Paginator传入两个参数,第一个是一个列表、元组或者查询结果集QuerySet,第二个是每页显示的数据,是一个整数
Paginator类中有三个常用属性:
Page对象:Paginator类提供一个**page(number)**函数,该函数返回的就是一个page对象,number表示第几个分页,在前端显示数据时,主要的操作都是基于Page()对象的。
Page对象有三个常用的属性:
Page对象还拥有几个常用的函数:
在view视图中,获取前端传过来的页面数据,包括页码数,每页条数,从数据库中查询数据,构建分页器,生成响应
def person_list(request):
page = int(request.GET.get("page, 10"))
per_page = int(request.GET.get("per_page"))
persons = Person.objects.all()
# 构建分页器
paginator = Paginator(persons, per_page)
# 前一步已经生成了全部的页面,我们直接获取具体的某一页
page_object = paginator.page(page)
# 生成响应
response = render(request, "person_list.html", locals())
return response
在html里面接受传入的页面数据
<ul>
{% for person in page_object.object_list %}
ul>
下面展示的是页码的生成,通过判断是否有前页后页,在第一和最后页时,将按钮变为不可点击状态。用到了bootstrap和后面要讲的反向解析
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page_object.has_previous %}
<li>
<a href="{% url 'two:persons' %}?page={{ page_object.previous_page_number }}&per_page={{ per_page }}"
aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{% endif %}
{% for page_num in paginator.page_range %}
<li><a href="{% url 'two:persons' %}?page={{ page_num }}&per_page={{ per_page }}">{{ page_num }}a>li>
{% endfor %}
{% if page_object.has_next %}
<li>
<a href="{% url 'two:persons' %}?page={{ page_object.next_page_number }}&per_page={{ per_page }}"
aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
{% endif %}
ul>
nav>
Django中的中间件也是面向切面编程的一种,注册在settings.py中的中间件会按照顺序加载执行,是django内置的一个底层插件,本质上是MiddlewareMixin的子类,是一个类装饰器,调用__call__
方法。
我们可以用中间件来实现类似于记录、日志、用户认证、黑白名单、优先级、拦截器、反爬虫等功能
内置的切点:
首先创建一个middleware的package,在理编写我们的功能代码
class CustomMiddleware(MiddlewareMixin):
# 重写process_request方法
def process_request(self, request):
print(request.path)
# 重写process_exception方法,出异常时重定向至首页
def process_exception(self, request, excception):
print(exception)
return redirect(reverse("app:index"))
然后在settings.py里面注册中间件
MIDDLEWARE = [
'middleware.LearnMiddlw.CustomMiddleware',
...
]
相对与HttpRequest来说,HttpRequest是Django根据request自动创建的,而HttpResponse是开发者自己创建的,我们编写的每个视图都要实例化、填充和返回一个HttpResponse对象。也就是函数的return值。
可传递的数据:
1.字符串:最简单的是传递一个定义的字符串返回
response = HttpResponse("This is a string to return", content_type("text/plain"))
也可以将它的实例化对象看做类文件写入:
response = HttpResponse()
response.write("Here is a title for a web page
")
2.可迭代对象:HttpResponse会立即处理这个迭代器,并把它的内容存成字符串,最后废弃这个迭代器。比如文件在读取后,会立刻调用close()方法,关闭文件。
3.设置头部字段:可以把HttpResponse对象当作一个字典一样,在其中增加和删除头部字段。
response = HttpResponse()
response["age"] = 18
del response["age"]
与字典不同的是,如果删除的头部字段不存在的话,会抛出KeyError,且不包含换行(CR、LF),会出现BadHeaderError异常
返回制定的数据类型content_type
,是可选的,用于填充HTTP的Content-Type
头部。如果未指定,默认情况下由DEFAULT_CONTENT_TYPE
和DEFAULT_CHARSET
设置组成:text/html; charset=utf-8
。
content_type
可以在MIME(多用途互联网邮件扩展)的概念中找到,他指定一个数据的类型和打开此数据的插件。
是HttpResponse的一个子类,默认content_type = "application/json"
,传入的参数是一个字典类型
# view视图中
data = {
"msg": "ok",
"status": 200
}
return JsonResponse(data)
重定向,可以根据url、第三方路径、命名空间、对象、视图view重定向
# 根据url路径
def my_view(request):
return redirect("/index/")
# 根据第三方路径
def my_view(request):
return redirect("heeps://www.cn.bing.com")
# 根据命名空间
def my_view(request):
return redirect(reverse("blog:article_list"))
根据对象重定向,前提是在模型中定义了get_absolute_url()方法,是定义Model的对象的查看地址,主要是用在RSS与后台查看:
在模型models.py中:
class Post(models.Model):
title = models.CharField('标题',max_length=200)
slug = models.CharField('slug',max_length=255,blank=True)
summary = models.TextField('摘要',blank=True)
body = models.TextField('正文')
def get_absolute_url(self):
return reverse('post_view',args=[self.slug])
视图views.py中:
def my_view(request):
obj = MyModel.objects.get(...)
return redirect(obj)
扩展:在模板中使用get_absolute_url()方法,在模板中生成标签时,使用这个方法而不用指明url路由的信息
<a href="{{ object.get_absolute_url }}">{{ object.name }}a>
正向解析就是根据url地址访问页面
反向解析就是根据命名空间定向到url
根路由Project/urls.py里面
urlpatterns = [
url(r'app/', include("app.urls"), namespace='app')
]
应用路由app/urls.py里面
urlpatterns = [
url(r'^index/', views.my_view, name='index')
]
在模板里面使用反向解析: