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" %}">
複製代碼
相關文章
相關標籤/搜索