Django中的V——视图

视图View

视图中对于数据库的操做请参考另外一篇博客,Django概述:blog.csdn.net/qq_27114273…html

request

request是Django框架根据Http请求报文生成的对象,包含了请求的全部信息,默认是视图函数的第一个参数前端

组成属性:python

  • path:请求页面的完整地址,不包括域名
  • method:请求方法,大写,'GET', 'POST'
  • GET:类字典对象,包含GET参数信息
  • POST:类字典对象,包含POST参数信息,可能为空
  • request:为了方便而建立,类字典对象,先搜索POST,而后GET,在高版本去除
  • cookies:标准Python字典,包含全部cookies
  • FILES:类字典对象,包含所上传的文件。
    • name:字符串上传文件的名
    • content-type:文件的内容类型
    • content:文件的原始内容
  • META:标准Python字典,包含全部HTTP头信息,完整地址,端口,语言,编码等
  • session:可读写的类字典对象,仅当激活session时有效
  • is_ajax:是不是ajax请求

几个方法:git

  • __getitem__(key):获取所给键的GET/POST值,先查找POST,而后GET,不存在则跑出keyerror
  • has_key():request.POST.has_key(),返回布尔值,是否包含所给的键
  • get_full_path():返回path,若请求参数有效,则会附带请求参数
  • is_secure():若是请求是HTTPS请求,返回True

QueryDict对象,是一个相似字典的类,被设计成能够处理同一个键有多个值的状况。它的实例是一个不可变对象,也就是说不能改变request.POST或者request.GET的值github

  • __getitem__(key):返回给定键的值,有多个就返回最后一个值
  • get(key, default):获取键的值,若是不存在返回默认值,相似于__getitem__()
  • getlist(key):获取键对应值的列表

处理文件

request请求上传的文件包含在FILES里面,键来自<input type="file" name=""/>中的name,只在POST请求中存在,而且提交的web

包含entype="multipart/form-data"时才生效,不然FILES只是一个空的类字典对象。

以图像为例,展现一个文件的上传和存储的过程。首先在视图函数中接受这个文件,具体实现的思路是,将文件拆分红小块的可迭代对象,而后将其写入到文件里面。ajax

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,分为加盐和不加盐,加盐的cookie更安全redis

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的参数:django

  • key:键
  • value:值
  • max_age:过时时间,时间为秒
  • expires:过时时间,为具体时间
  • path:生效路径
  • domain:生效的域名
  • secure:HTTPS请求时设置为True
  • httponly:用于http传输,JavaScript没法获取

session

默认

session是数据保存在服务器的回话技术,flask默认将session存在了cookie中,django默认存在了ORM中,在迁移时默认生成一张django-session的表,django将session持久化到了内存中。

主要有三个字段:

  • session_key:惟一
  • session_data:数据拼接混淆串,跟base64编码的串结合在一块儿
  • session_expire:默认过时时间14天
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>

复制代码

redis

session实如今redis中存取借助了一个模块pip install django-redis-sessions,或者在下载页面下载而后python setup.py install,github地址:github.com/martinrusev… ,安装以后须要在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会自动帮咱们完成。

cache

默认

Django是自带缓存系统的,默认将缓存放在的了配置的数据库中,在终端命令行里面可使用默认命令建立缓存表,python manager.py createcachetable TableName,会在数据库中生成一张自定义名称TableName的表,用来存储缓存,包含三个参数,都不容许为空

  • cache_key
  • value
  • expires

须要在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

用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类中有三个经常使用属性:

  • count:表示全部页面对象的总数
  • num_page:表示页面总数
  • page_range:下表从1开始的页面范围迭代器

Page对象:Paginator类提供一个**page(number)**函数,该函数返回的就是一个page对象,number表示第几个分页,在前端显示数据时,主要的操做都是基于Page()对象的。

Page对象有三个经常使用的属性:

  • object_list:表示当前页面上全部对象的列表
  • numberv:表示当前也的序号,从1开始计数
  • paginator:当前Page对象所属的Paginator对象

Page对象还拥有几个经常使用的函数:

  • has_next():判断是否有下一页,有就返回True
  • has_previous():判断是否有上一页,有就返回True
  • has_other_pages():判断是否有上一页或下一页,有就返回True
  • next_page_number():返回下一页的页码,若是下一页不存在抛出InvalidPage 异常
  • previous_page_number():返回上一页页码,若是上一页不存在抛出InvalidPage 异常

在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">&laquo;</span>
                </a>
            </li>
        {% else %}
            <li class="disabled">
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</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">&raquo;</span>
                </a>
            </li>
        {% else %}
            <li class="disabled">
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
        {% endif %}
    </ul>
</nav>
复制代码

中间件

Django中的中间件也是面向切面编程的一种,注册在settings.py中的中间件会按照顺序加载执行,是django内置的一个底层插件,本质上是MiddlewareMixin的子类,是一个类装饰器,调用__call__方法。

咱们能够用中间件来实现相似于记录、日志、用户认证、黑白名单、优先级、拦截器、反爬虫等功能

内置的切点:

  • process_request
  • process_view
  • process_template_response
  • process_response
  • process_exception
    • 界面友好化
    • 错误记录

首先建立一个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',
    ...
]
复制代码

返回Response

HttpResponse

相对与HttpRequest来讲,HttpRequest是Django根据request自动建立的,而HttpResponse是开发者本身建立的,咱们编写的每一个视图都要实例化、填充和返回一个HttpResponse对象。也就是函数的return值。

可传递的数据:

  • 字符串
  • 可迭代对象
  • 设置头部字段

1.字符串:最简单的是传递一个定义的字符串返回

response = HttpResponse("This is a string to return", content_type("text/plain"))
复制代码

也能够将它的实例化对象看作类文件写入:

response = HttpResponse()
response.write("<p>Here is a title for a web page</p>")
复制代码

2.可迭代对象:HttpResponse会当即处理这个迭代器,并把它的内容存成字符串,最后废弃这个迭代器。好比文件在读取后,会马上调用close()方法,关闭文件。

3.设置头部字段:能够把HttpResponse对象看成一个字典同样,在其中增长和删除头部字段。

response = HttpResponse()
response["age"] = 18
del response["age"]
复制代码

与字典不一样的是,若是删除的头部字段不存在的话,会抛出KeyError,且不包含换行(CR、LF),会出现BadHeaderError异常

返回制定的数据类型content_type,是可选的,用于填充HTTP的Content-Type头部。若是未指定,默认状况下由DEFAULT_CONTENT_TYPEDEFAULT_CHARSET设置组成:text/html; charset=utf-8

content_type能够在MIME(多用途互联网邮件扩展)的概念中找到,他指定一个数据的类型和打开此数据的插件。

JsonResponse

是HttpResponse的一个子类,默认content_type = "application/json",传入的参数是一个字典类型

# view视图中
data = {
    "msg": "ok",
    "status": 200
}
return JsonResponse(data)
复制代码

redirect

重定向,能够根据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')
]
复制代码

在模板里面使用反向解析:

<!--反向解析到应用app的index路由中-->
<a href="{% url "app:index" %}">
复制代码
相关文章
相关标签/搜索