輕量級Django學習(2)——無狀態的Web應用

佔位圖片服務器

  • 一個典型的佔位圖片服務器,接收一個指定圖片大小的URL並生成該圖片。全部建立 請求圖片所需的信息的包含在URL,無需進行權限驗證,是一個很好的無狀態應用的例子。

HTTP自己是個無狀態的協議,就是說,每個到達服務器的請求都獨立於以前的請求。若是須要特定的狀態,則須要把它加到應用層上css

Django這樣的框架使用Cookie及其餘機制將統一客戶端發送的請求綁定在一塊兒。html

  • 利用模板建立項目

藉助在Django學習1中的模板,建立一個placeholder的新項目python

django-admin startproject placeholder --template=project_name
  • 視圖與URL模式web

    咱們用兩個視圖來生成響應,一個視圖用來按照請求的寬度高度渲染佔位圖像,另外一個用來渲染主頁面內容。正則表達式

def placeholder(request,width,height):
   # TODO : Rest of the view will go here
   return HttpResponse('OK')
def index(request):
   return HttpResponse('Hello World')

咱們還要一個指向placeholder視圖的路由,包含兩個參數:widthheight,Django的URL模式使用正則表達式來匹配輸入的URLdjango

# 關聯URL模式,捕捉的模式組會以位置參數的形式傳遞給視圖,
# 命名的組會以關鍵字參數的形式傳遞,經過?P語法來捕獲被命名的組,
# 並用[0-9]來匹配任意數字字符
urlpatterns = (
   url(r'^image/(?P<width>[0-9]+)×(?P<height>[0-9]+)/',placeholder,name='placeholder'),
   url(r'^$',index,name='homepage'),
)

完成模式的設置後,像**/image/30×25/的這樣的URL會被路由到placeholder中,並傳遞這些數值(width=30、height=50)。同時以homepage爲名稱與placeholder視圖新的路由一塊兒被加入index**路由上。瀏覽器

  • 佔位視圖

咱們說到佔位視圖接收兩個整數參數來設置圖片的高度和寬度。可是儘管正則表達式保證了高度寬度只包含整數,但它們還是以字符串的形式傳遞給視圖的。爲了確保在一個可管理的尺寸內,咱們用表單來實現驗證。緩存

典型的表單用於驗證POSTGET內容,它們也能夠用於驗證URL和cookie的特定值。服務器

from django.http import HttpResponse,HttpResponseBadRequest
from django.conf.urls import url
from django.core.wsgi import get_wsgi_application
from django import forms
# 視圖
class ImageForm(forms.Form):
   height = forms.IntegerField(min_value=1, max_value=2000)
   width = forms.IntegerField(min_value=1, max_value=2000)
def placeholder(request,width,height):
   form = ImageForm({'height':height,'width':width})
   if form.is_valid():
       height = form.cleaned_data['height']
       width = form.cleaned_data['width']
       # TODO : Generate image of requested size
       return  HttpResponse('OK')
   else:
       # 若是表單無效,視圖發送錯誤信息返回客戶端
       return HttpResponseBadRequest('Invalid Image Request')
  • 圖片處理

要生成實際的圖片,咱們還須要安裝Pillow模塊來處理圖片:cookie

pip install Pillow

經過Pillow建立圖片須要兩個參數,一個以元組表示的顏色模式和尺寸。

咱們在placeholder.py中的視圖使用RGB模式和在表單中處理過的數據尺寸

from io import  BytesIO
from PIL import Image
class ImageForm(forms.Form):
   height = forms.IntegerField(min_value=1, max_value=2000)
   width = forms.IntegerField(min_value=1, max_value=2000)

   def generate(self,image_format='PNG'):
       height = self.cleaned_data['height']
       width = self.cleaned_data['width']
       image = Image.new('RGB',(width,height))
       content = BytesIO()
       image.save(content,image_format)
       content.seek(0)
       return content
def placeholder(request,width,height):
   form = ImageForm({'height':height,'width':width})
   if form.is_valid():
       # 視圖調用from.generate 來獲取建立的圖片,圖片的字節被用於以後建立響應體
       image = form.generate()
       return  HttpResponse(image,content_type='image/png')
   else:
       return HttpResponseBadRequest('Invalid Image Request')

Image 中加入了一個新的generate方法,用於封裝建立圖片的邏輯,它接收一個參數來設置圖片格式,默認爲PNG,並以字節的形式返回圖片內容。

表單驗證圖片尺寸經過後,視圖會成功返回一張請求寬度和高度的PNG圖片。圖片內容不寫入磁盤,直接發送給客戶端。

咱們還可使用ImageDraw模塊在圖片中加入文字,如:

from PIL import Image,ImageDraw
 def generate(self,image_format='PNG'):
     height = self.cleaned_data['height']
     width = self.cleaned_data['width']
     image = Image.new('RGB',(width,height))
     # 使用ImageDrawm模塊在圖片中加入文字
     draw = ImageDraw.Draw(image)
     text = '{}×{}'.format(width,height)
     textwidth,textheight = draw.textsize(text)
     if textwidth < width and textheight < height :
        # // 是整數除法
        texttop = (height - textheight) // 2
        textleft = (width - textwidth) // 2
       # 向尺寸合適的地方加入覆蓋文字
        draw.text((textleft,texttop),text,fill=(255,255,255))
     content = BytesIO()
     image.save(content,image_format)
     content.seek(0)
     return content
  • 加入緩存

每次請求視圖時,佔位圖片視圖都會從新生成圖片。因爲圖片的寬度和高度是由最初值來設置的,經常會對服務器提出沒必要要的請求。

緩存是避免這種重複的重要辦法,當要肯定如何爲服務提供緩存時,有兩種方案可供考慮:服務器端客戶端

對於服務器端緩存,能夠方便地使用Django地緩存工具。這會把內存開銷轉換爲緩存存儲,同時會節約生成圖片所需的CPU週期。

``` python

from django.core.cache import cache def generate(self,image_format='PNG'): height = self.cleaned_data['height'] width = self.cleaned_data['width'] # 經過寬度、高度和圖片格式生成一個緩存鍵值 key = '{}.{}.{}'.format(width,height,image_format) # 新圖片建立前,檢查緩存是否已經存儲了圖片 content = cache.get(key) if content is None: image = Image.new('RGB',(width,height)) # 使用ImageDrawm模塊在圖片中加入文字 draw = ImageDraw.Draw(image) text = '{}×{}'.format(width,height) textwidth,textheight = draw.textsize(text) if textwidth < width and textheight < height : # // 是整數除法 texttop = (height - textheight) // 2 textleft = (width - textwidth) // 2 draw.text((textleft,texttop),text,fill=(255,255,255)) content = BytesIO() image.save(content,image_format) content.seek(0) # 當未找到緩存圖片且建立了新圖片時,經過鍵值將圖片保存一小時 cache.set(key,content,60 * 60) return content ```

緩存的另外一個方案,就是關注客戶端性能並使用瀏覽器內建的緩存。Django引入了了一個建立並使用視圖的ETag標頭的etag修飾符。該修飾符接收一個參數,一個從請求和視圖參數中生成ETag標頭的函數。

import os
import hashlib
from django.views.decorators.http import etag
# generate_etag是個新函數,接收placeholder視圖中的參數
# 使用hashlib來返回一個基於width和height值變化的不透明的ETag值
def generate_etag(request,width,height):
   content = 'Placeholder:{0}×{1}'.format(width,height)
   return  hashlib.sha1(content.encode('utf-8')).hexdigest()
# generate_etag函數會被髮送到placeholder視圖的etag修飾符中
@etag(generate_etag)
def placeholder(request,width,height):

使用etag修飾符具備在視圖被訪問以前進行ETag計算的優點。

  • 主頁面視圖

完成佔位視圖後,須要渲染一個基本的HTML模板。首先咱們加入StaticTemplate設置。讓Django能夠按照路徑找到模板和靜態資源。添加資源後的目錄應該是這樣的:

placeholder/
   placeholder.py
   templates/
       home.html
   static/
       site.css

手動配置路徑時,爲了不對路徑的硬性編碼,使用Python標準庫中的os模塊

import os
import hashlib
import sys
# 附加配置
DEBUG = os.environ.get('DEBUG','on') == 'on'
# 隨機的祕鑰
SECRET_KEY = os.environ.get('SECRET_KEY','p*0c^!*uu70+vqq%y@xp66g*n8i9$au9$nx+d4p))6d_&kl@nb')
BASE_DIR = os.path.dirname(__file__)
# 設置
settings.configure(
   DEBUG = DEBUG,
   SECRET_KEY = SECRET_KEY,
   ROOT_URLCONF = __name__,
   MIDDLEWARE_CLASSES = (
       'django.middleware.common.CommonMiddleware',
       'django.middleware.csrf.CsrfViewMiddleware',
       'django.middleware.clickjacking.XFrameOptionsMiddleware',
   ),
   INSTALLED_APPS=(
     'django.contrib.staticfiles',
   ),
   TEMPLATES=(
       {
           'BACKEND':'django.template.backends.django.DjangoTemplates',
           'DIRS':(os.path.join(BASE_DIR,'templates'),),
       },
   ),
   STATICFILES_DIRS=(
       os.path.join(BASE_DIR,'static'),
   ),
   STATIC_URL = '/static/',
)

最後,咱們加入簡單的主頁面模板和CSS文件,

** home.html**:

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="utf-8">
   <title>Django Placeholder Images</title>
   <link rel="stylesheet" href="{% static 'site.css' %}" type="text/css">
</head>
<body>
   <h1>Django Placeholder Images</h1>
   <p>This server can be used for serving placeholder
   images for any web page.</p>
   <p>To request a placeholder image of a given width and height simply include an image with the source pointing to
   <b>/image/&lt;width&gt;x&lt;height&gt;/</b>
   on this server such as:</p>
   <pre>
       &lt;img src="{{ example }}" &gt;
   </pre>
   <h2>Examples</h2>
   <ul>
       <li><img src="{% url 'placeholder' width=50 height=50 %}"></li>
       <li><img src="{% url 'placeholder' width=100 height=50 %}"></li>
       <li><img src="{% url 'placeholder' width=50 height=100 %}"></li>
   </ul>
</body>
</html>

site.css:

body{
   text-align: center;
}
ul{
   list-type:none;
}
li{
   display: inline-block;
}

固然,咱們要在placeholder.py中更新index視圖來渲染這個模板。

from django.core.urlresolvers import reverse
from django.shortcuts import render
#更新index視圖來渲染模板
def index(request):
   # 更新過的index視圖經過翻轉placeholder視圖來建立一個URL樣例
   # 並將它傳給模板上下文
   example = reverse('placeholder',kwargs={'width':50,'height':50})
   context = {
       'example':request.build_absolute_uri(example)
   }
   # 經過render快捷方式來渲染home.html模板
   return render(request,'home.html',context)
  • 運行

    最後運行完整的項目查看建立的佔位圖片服務

python placeholder.py runserver

結果: 輸入圖片說明

  • 問題總結

應用在python3環境下順利跑通,在python2.7下會遇到字符編碼問題

相關文章
相關標籤/搜索