Django框架解析

前端相關

展示內(nèi)容:瀏覽器接收后端返回的html文本(經(jīng)過模板渲染)內(nèi)容并在頁面展示.
與用戶交互信息:js將用戶產(chǎn)生數(shù)據(jù)通過form表單形式或者ajax形式將數(shù)據(jù)傳輸?shù)胶笈_并接收返回信息,從而達到數(shù)據(jù)交互的作用.


請求的本質(zhì)

CS架構(gòu):本質(zhì)上django程序就是一個socket服務端汰瘫,瀏覽器其實就是一個socket客戶端.
django自帶的wsgi模塊處理瀏覽器的請求信息掂榔,用戶只需要實現(xiàn)路由和視圖函數(shù)驻襟、模板等代碼部分.


django請求的生命周期

指當用戶在瀏覽器上輸入url到用戶看到網(wǎng)頁的這個時間段內(nèi)疲吸,Django程序內(nèi)部所發(fā)生的事情,具體步驟如下:

  1. 當用戶在瀏覽器輸入url時, 然后瀏覽器會生成請求頭和請求體發(fā)送給服務器;
  2. url經(jīng)過wsgi---中間件---最后到達路由映射表赃承,隨后按順序進行正則匹配,若匹配到,則進入對應的視圖函數(shù)或者類(CBV)中悴侵;
  3. 視圖函數(shù)根據(jù)客戶端的請求,取出數(shù)據(jù)并渲染到模板中,其中可能涉及ORM操作(增刪改查)或從緩存取出數(shù)據(jù), 最終以字符串的形式返回.
image

Form

表單提交瞧剖,頁面與web服務器交互數(shù)據(jù)最重要的方式.
基礎信息詳見:http://www.cnblogs.com/fqh202/p/8483862.html

Django之Form組件

根據(jù)創(chuàng)建的form類,可以在頁面自動生成form的input標簽.

創(chuàng)建form類:
可以直接在app目錄下新建forms.py文件.
在類中定義input標簽.

from django.forms import Form
from django.forms import widgets
from django.forms import fields


class MyForm(Form):
    username = fields.CharField(
        # 可以對輸入的數(shù)據(jù)進行初步篩選
        max_length=6,
        min_length=2,
        widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})
    )

    # 多選一
    gender = fields.ChoiceField(
        choices=((1, '男'), (2, '女'),),
        initial=2,
        widget=widgets.RadioSelect
    )

    city = fields.CharField(
        initial=2,
        widget=widgets.Select(choices=((1, '上海'), (2, '北京'),)) # 下拉框
    )

    pwd = fields.CharField(
        min_length=6,
        widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )

構(gòu)建views.py邏輯
導入自定義的form類并實例化:obj = MyForm()
若填充數(shù)據(jù)可免,is_valid()根據(jù)自定義的規(guī)則對表單數(shù)據(jù)進行驗證抓于,沒有錯誤則通過obj.clean()以字典形式取出數(shù)據(jù),若有錯誤浇借,則錯誤信息以字典形式保存在obj.errors中.

from django.shortcuts import render, redirect
from .forms import MyForm


def login(request):
    if request.method == "GET":
        obj = MyForm()
        return render(request, 'login.html', {'form': obj})

    elif request.method == "POST":
        obj = MyForm(request.POST, request.FILES)
        errors={}
        if obj.is_valid():
            values = obj.clean()
            print(values)
        else:
            errors = obj.errors
            print(errors)
        return render(request, 'login.html', {'form': obj,'errors':errors})

    else:
        return redirect('http://www.baidu.com')

在login.html動態(tài)生成input標簽:可以很方便的顯示錯誤信息.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登錄頁面</title>
    <style>
        div{
            margin-bottom: 10px;
        }
        .error{
            font-size: 12px;
            color: red;
        }
    </style>
</head>
<body>


<form method="post" novalidate>
    {# 1.常規(guī)制作input標簽 #}
        {#    <label for="id_username">用戶名</label><input type="text" id="id_username" name="username">#}
        {#    <label for="id_password">密碼</label><input type="text" id="id_password" name="password">#}
        {#    <input type="submit">#}

    {# 2.模板語言結(jié)合自定義form動態(tài)生成input標簽 #}
    <div>用戶名{{ form.username }}<span class="error">{{ form.username.errors.0 }}</span></div>{# 可以直接顯示錯誤信息 #}
    <div>性別{{ form.gender }}</div>
    <div>城市{{ form.city }}</div>
    <div>密碼{{ form.pwd }}<span class="error">{{ form.pwd.errors.0 }}</span></div>
    <input type="submit">
    {% csrf_token %}
</form>


</body>
</html>

常用控件

// radio單選按鈕捉撮,值為字符串==========================
gender = forms.ChoiceField(
    choices=((1, '男性'),(2, '女性'), (3, '中性'), ), initial=1, 
    widget=widgets.RadioSelect
)

gender = forms.CharField(
    initial=1, widget=widgets.RadioSelect(choices=((1, '男性'),(2, '女性'), (3, '中性'), ))
)


// 單select,值為字符串========================================
user = fields.CharField(
     initial=2,
     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

user = fields.ChoiceField(
     choices=((1, '上海'), (2, '北京'),),
     initial=2,
     widget=widgets.Select
)
 

// 多選select妇垢,值為列表==========================================
user = fields.MultipleChoiceField(
     choices=((1,'上海'),(2,'北京'),),
     initial=[1,],
     widget=widgets.SelectMultiple
)
 

// 單checkbox
gender = forms.ChoiceField(initial=[2, ],choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxInput)


// 多選checkbox,值為列表
user = fields.MultipleChoiceField(
     initial=[2, ],
     choices=((1, '上海'), (2, '北京'),),
     widget=widgets.CheckboxSelectMultiple
)

自定義不能為空的錯誤信息

class RegisterForm(forms.Form):
    username = forms.CharField(min_length=2,error_messages={'required':'用戶名不能為空'},

正則自定義錯誤信息
1.存在多條驗證時, 以列表或者元祖形式導入實例化的RegexValidator對象, django會按照順序逐個驗證,直到拋出錯誤信息;
2.傳入兩個參數(shù): 匹配的正則表達式錯誤信息;
3.原理是若輸入與正則不匹配,則拋出ValidationError(錯誤信息)

from django.form import Form
from django.core.validators import RegexValidator

class UserForm(Form):
    username = forms.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '請輸入數(shù)字'), 
                    RegexValidator(r'^159[0-9]+$', '數(shù)字必須以159開頭')],
        widget=widgets.TextInput(attrs={'class': 'form-control'}))

自定義驗證函數(shù)

from django.core.exceptions import ValidationError
import re

def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手機號碼格式錯誤')


class UserForm(forms.Form):
    username = forms.CharField(validators=[mobile_validate, ], widget=widgets.TextInput(attrs={'class': 'form-control'}),)

局部鉤子:重載內(nèi)置的clean_field()方法巾遭,在form字段中定義的驗證完成后,會執(zhí)行clean_field()方法闯估,此時通過cleaned_data取出值進行判斷.

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError

class UserForm(Form):
    username = forms.CharField(widget=widgets.TextInput(attrs={'class': 'form-control'}),)
    
    def clean_username(self):
        value = self.cleaned_data['username']
        if value == 'alex':
            raise ValidationError('用戶名已存在')
        return value

全局鉤子
重載系統(tǒng)的clean()方法(clean(self)是在clean_field(self)之后運行的).
錯誤信息是保存在obj.errors.__all__中灼舍,可以通過自定義過濾器取出自定義的錯誤信息并在前端顯示.

# forms.py
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator,ValidationError

class MyForm(Form):
   ....
    def clean(self):
        if self.cleaned_data.get('pwd1') != self.cleaned_data.get('pwd2'):
            raise ValidationError('密碼不一致')
        else:
            return self.cleaned_data



// 自定義過濾器
from django import template
register = template.Library()

@register.filter
def get_error(error_dict):
    if error_dict.get('__all__'):
        return error_dict.get('__all__')[0]

// 前端顯示自定義錯誤信息
 <div id="errors">
     {% if errors %}
        {{ errors|get_error }}
     {% endif %}
 </div>
文件上傳

指定傳輸數(shù)據(jù)處理方式: enctype="multipart/form-data"
取出文件:文件的二進制格式保存在request.FILES中,在后臺直接以字典方式取出.

django配置media上傳文件路徑

// 1. setting.py配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media', "fileholder_name", ...) # 若存在多級目錄,可以逐個寫入文件夾名稱即可

// 2. urls.py配置
from django.views.static import serve
from onlineLearningSys import settings
# 僅限于debug模式
urlpatterns += [
    url(r'^media/(?P<path>.*)$', serve, {
        'document_root': settings.MEDIA_ROOT,
    }),
]


// 3. models.py
# 圖片是以路徑字符串的形式存儲在數(shù)據(jù)庫的;
image = models.ImageField(upload_to="user/%Y/%m", verbose_name='圖片', max_length=100, default='static/images/xgt.jpg')


// 4. html文件,類似于imageField存儲的格式是: organization/2018/03/1499239092.png, 必須在加上media前綴
 <a href="org-detail-homepage.html">
    <img width="200" height="120" class="scrollLoading" data-url="/media/{{ org.org_image }}"/>
</a>

后臺取出文件并利用model方式保存

# views.py 保存用戶圖片信息
def ***()
    img = request.FILES.get('img')
    user = UerProfile.objects.create_user(
        username=username,
        password=password,
        img= img,
    )
    user.save()

手動保存文件:很少用到涨薪!

file_obj = request.FILES.get('upload_file')
f1 = open(file_obj.name, "wb")

# 方法1
for i in file_obj:
    f1.write(i)
    
# 方法2
for i in file_obj.chunks():
    f1.write(i)

補充

在form表單中骑素,只有<input type="button" id="btn" vaule='提交'>觸發(fā)的事件不會自動提交表單,<button><input type="submit" id="btn">都會自動提交表單刚夺,切記献丑!

<form>
    <input type="text">
    <input type="button" id="btn" value='提交'>
</form>

Ajax

通過js或者jquery在當前頁面獲取局部的數(shù)據(jù),并將提交到后臺.
異步傳輸:不會阻塞進程光督。
局部刷新:不會刷新整個頁面阳距。

Jquery實現(xiàn)Ajax發(fā)送

1.常規(guī)方法
手動構(gòu)建data,包括跨域請求csrf鍵值對.
只能傳輸簡單的數(shù)據(jù)结借,不能傳輸文件筐摘,傳輸?shù)胶笈_的是文件的路徑.

<script>
$('#submit_btn').click(function () {
    $.ajax({
       url:'/login.html/',
       type:"POST",
       data: {'username':$('#username').val(), 
            'password': $("#password").val(),
            "csrfmiddlewaretoken":$('[name="csrfmiddlewaretoken"]').val()
       },
       success:function (data) {
               alert(data)
           }
           
    })
});
</script>

2.serialize方法
直接將form標簽序列化:data: $('.post_form').serialize(),
相對更簡單,同樣不能傳遞文件.

3.formdata
構(gòu)建:var form = new FormData()
有手動填充和自動填充兩種方式.
可以傳輸文件

// 手動填充form
$('#submit_btn').click(function () {
     var form = new FormData();
      form.append("username",$('#id_username').val());
      form.append("password",$('#id_password').val());
      form.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val());
      
      # 文件傳輸;
      form.append('file',$('#upload_file')[0].files[0]);
      
     $.ajax({
         url: '/login.html/',
         type: 'post',
         data: form,
         processData: false,
         contentType:false,
         success: function (data) {
         }
     })
 });
            

// 自動填充
$('#submit_btn').click(function () {
    var form = new FormData($('#login_form')[0]); // 注意,必須傳入DOM對象
    
    $.ajax({
     url: '/login.html/',
     type: 'post',
     data: form,
     processData: false,
     contentType:false,
     success: function (data) {
        alert(data)
     }
    
     })
});
JS實現(xiàn)Ajax發(fā)送
json簡單介紹

javascript object notation縮寫船老,基于javascript語言的輕量級數(shù)據(jù)交換格式.
作用:可靠性傳遞數(shù)據(jù)咖熟,不改變其類型,例如字典和列表.

dict >> str >> bytes>> str >>> dict
json.dumps() encode/decode json.loads()

兩種格式

  1. {key:value, ...}:key是字符串,value可以為所有類型;
  2. [{k1:v1}, {k2:v2}, ...]

類型string, number, boolean, array, object, null

易混淆概念:
字符串:雙引號或單引號包括的字符.
json字符串:符合json格式的字符串.
json對象:指符號json要求的js對象.

在js中使用方法

# 創(chuàng)建json對象
var obj = {
    key: value, 
    k2: [], 
    k3: {k4:v4,...}
}

# 查詢:obj.key

# 增加鍵值對或修改:obj.key = v2

# 刪除:delete obj.key

靜態(tài)文件配置

  1. 將所有靜態(tài)文件放到一個static文件夾下, 主要是項目固有的js, css, img等不會更改的文件柳畔;

  2. 每個應用都應該有自己的靜態(tài)文件夾馍管;

  3. settings.py配置

    STATIC_URL='static/'
    STATICFILES_DIRS=[os.path.join(BASE_DIR, 'static')]
    
  4. 前端引入:

    {% load staticfiles %}
    <link rel="stylesheet" href={% static 'css/bootstrap.css' %}">
    

Cookies & Session

由于WEB是無狀態(tài)請求, 服務器不能直接識別用戶的狀態(tài)信息。

cookies簡單使用

獲取Cookies: request.COOKIES.get("islogin",None)薪韩。
設置Cookiesobj.set_cookie("islogin",True)确沸。
刪除Cookies: obj.delete_cookie("cookie_key",path="/",domain=name)

實現(xiàn)cookies的簡單用戶認證登錄

# views.py
def index(request):
    # 從本地的cookie查詢,若找到對應的用戶,則直接跳轉(zhuǎn)主頁面,否則重新登錄
    is_login = request.COOKIES.get("bob", None)
    if is_login:
        return render(request, 'index.html')
    else:
        return redirect('/login/')

def my_login(request):
    if request.method=='POST':
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        if username == 'bob' and pwd == 'abc123':
            obj = redirect("/index/")
            obj.set_cookie("bob", 'abc123', max_age=10)  # 設置cookie鍵值對,會保存在本地, 最大期限為10s;
            return obj
            
    return render(request, 'login.html')
cookies 和 session 結(jié)合使用

COOKIE:緩存在本地捌锭,每次請求攜帶信息{SESSION_ID: 'ABC'}
SESSION:保存在服務器罗捎,即為SESSION表

SESSION_KEY = 'ABC'
SESSION_DATA = HASH({'DATA1', 'K2':'DATA2', ...})

流程
1.客戶端首次發(fā)送請求未攜帶cookie信息观谦,django會給客戶端返回cookie鍵值對{session_id:'隨機字符串'},作為客戶端瀏覽器標識桨菜;
2.用戶登錄豁状,設置session存儲用戶狀態(tài)信息:

authenticate(username='', password='')  # 檢驗取出用戶對象, 
login(request, user)  # 登錄,生成session記錄

3.當客戶端再訪問server則根據(jù)攜帶的cookie從session中取出對應的用戶信息,也就是本地瀏覽器在session的有效期內(nèi)再次訪問服務器,無需登錄,可以直接訪問授權(quán)的頁面倒得。具體用戶的狀態(tài)信息怎么保存在session_data中,暫時不深究!

session基本操作

# 1.設置session值
request.session["session_name"]="admin"
# 2.獲取session值
session_name = request.session("session_name")
# 3.刪除session值
del request.session["session_name"]  刪除一組鍵值對
request.session.flush()   刪除一條記錄
# 4. 檢測是否操作session值
`if "session_name"  is request.session:`
# 5. 取值
get(key, default=None)
fav_color = request.session.get('fav_color', 'red')

# 6.pop(key)
fav_color = request.session.pop('fav_color')
# 7泻红、keys()
# 8、items()
10霞掺、flush() # 刪除當前的會話數(shù)據(jù)并刪除會話的Cookie谊路,django.contrib.auth.logout() 函數(shù)中就會調(diào)用它。
# 用戶session的隨機字符串
request.session.session_key
# 將所有Session失效日期小于當前日期的數(shù)據(jù)刪除
request.session.clear_expired()
# 檢查 用戶session的隨機字符串 在數(shù)據(jù)庫中是否
request.session.exists("session_key")
# 刪除當前用戶的所有Session數(shù)據(jù)
request.session.delete("session_key")

session和cookies登錄實例

# 視圖函數(shù)views.py

def index(request):
    # 直接取出之前存儲的值
    if not request.session.get("is_login"):
        return redirect('/login/')
    else:
        return render(request, 'index.html')
    

def login_(request):
    if request.method == "GET":
        return render(request, 'login.html')
        
    username = request.POST.get('username')
    pwd = request.POST.get('password')
    if username == 'kate' and pwd == 'abc123':
        # 此時cookie信息django已經(jīng)自動生成
        print(request.COOKIES)
        # {'csrftoken':'3KV1lSv2JqMVxTOtq1YNiUh8OjmhwACqoyCdSxwgCAcIK6NWWIozg1WtiMN4zfXL'}
        
        # 需要我們自己設置session存儲的狀態(tài)信息
        print(request.session)  # <django.contrib.sessions.backends.db.SessionStore object at 0x00000000042E77F0>
        request.session['is_login'] = True
        request.session['user'] = 'kate'
        return redirect('/index/')

后端

django相關命令
下載: pip install django
創(chuàng)建項目: django-admin.py startproject pro_name
創(chuàng)建應用: python manage.py startapp appname

中間件Middlewares

位于wsgi和路由映射之間的一個模塊根悼,能夠在全局上改變django的輸入與輸出凶异,例如用戶信息驗證/日志記錄等.
中間件方法: process_requestprocess_response挤巡,

自定義中間件

1.在項目目錄下創(chuàng)建文件夾utils, 內(nèi)部創(chuàng)建my_middleware.py文件, 寫入類Md1, 文件名稱和類名稱自定義;

from django.utils.deprecation import MiddlewareMixin

class Md1(MiddlewareMixin):
    def process_request(self, reqeust):
        print('md1_process_request')

    def process_response(self, requst, response):
        print('md1_process_response')
        return response  # 必須帶返回值


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print('md2_process_request')

    def process_response(self, request, response):
        print('md2_process_response')
        return response  # 必須帶返回值

2.在配置文件中加入加上的中間件:

MIDDLEWARE = [
'utils.my_middleware.Md1',
'utils.my_middleware.Md2',
...
]

3.此時django所有的請求和相應都會依次經(jīng)過process_request()process_response函數(shù).

# 結(jié)果
md1_process_request
md2_process_request
md2_process_response
md1_process_response

process_request返回值
默認為return None,當需要過濾某些請求時酷麦,可以直接return.
當存在返回值矿卑,就不會繼續(xù)往下執(zhí)行,就地返回相應的返回值至瀏覽器.

class Md1(MiddlewareMixin):
    def process_request(self, request):
        print('md1_process_request')
        return HttpResponse('<h1>你好</h1>')

process_response返回值
返回值就是Httpresponse返回的字符串沃饶,返回的字符串會經(jīng)過中間件處理, 例如加上響應頭數(shù)據(jù)母廷,必須帶返回值, 否則會出現(xiàn)系統(tǒng)錯誤.

版本向后兼容:為提高兼容性, 直接在自定義的Md上方寫入以下源碼,直接繼承, 此時就無須導入(有時候版本不同, 文件位置會有變動)

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response
自定義中間件實現(xiàn)用戶登錄
# my_middleware.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render

class Md1(MiddlewareMixin):

    def process_request(self, request):
        print('md1_process_request')
        # 若本次請求是login頁面, 則當前中間件不做任何操作
        if request.path == '/login.html':
            return None
        # 若未取不到對應的信息,則跳轉(zhuǎn)至登錄頁面,此處直接render
        if not request.session.get('user_info'):
            return render(request, 'login.html')

    def process_response(self, request, response):
        print('md1_process_response')
        return response  # 必須帶返回值

在配置文件中,自定制中間件盡可能放在尾部,因為必須經(jīng)過django自帶的中間件處理再進行自定義判斷.
注意重定向的問題糊肤,若取不到session信息就直接redirect登錄url的話,就會出現(xiàn)無限從定向問題,因為下一次回到這里還是沒有session信息,所以直接render頁面,避免此問題;

process_view方法

到達路由映射匹配視圖函數(shù)時又再一次有上到下運行每個中間件的process_view方法.
process_view函數(shù)默認return None琴昆,若帶返回值的話,那么后面中間件的process_view都不會執(zhí)行, 直接跳轉(zhuǎn)到尾部執(zhí)行process_response, 返回值為process_view的返回值.

process_exception捕捉異常

捕捉請求url相關的視圖函數(shù)中的異常馆揉,若沒有發(fā)生異常,此函數(shù)是不會運行的.
中間件會按照由下至上的順序執(zhí)行此方法业舍,若存在返回值,則直接將返回值交給process_response返回,但是上面的中間件仍然會執(zhí)行完此方法!

[圖片上傳失敗...(image-74d451-1530180354286)]


路由映射urls

url路徑與視圖一一對應的關系.
傳過來的請求路徑與urls中的正則從上至下匹配,若匹配到則直接調(diào)動對應的視圖函數(shù).

命名

name:對url命名升酣,當url有變動時候舷暮,無須修改其他地方的引用!

# urls.py
url(r'^test/(?P<i>\d{1,4})', app1_view.test, name='test'),

# index.html 在頁面直接通過name屬性定位url噩茄,且可以傳遞一個參數(shù)
<a href="{% url 'test' 111 %}">點我</a>

namespace:在include中使用下面,針對多層級urls.py.
初步匹配后進入include中的app的urls進一步匹配;
在利用模板語言標簽{% url 'namespace: name' %}可以調(diào)用,保證url的唯一性.

# 項目主目錄
from django.conf.urls import url,include
urlpatterns = [
    url(r'^user/', include('app01.urls',namespace='users')),
]

# app01.urls
from django.conf.urls import url,include
urlpatterns = [
    url(r'^register/(\d+)/', app1_view.register,name='register'),
]

# index.html 點擊a標簽會直接定位到對應的url
<a href="{% url 'users:register' %} ">點我2</a>
請求路徑相關

<a href='/login/'>:以斜杠開始的路徑, 直接當前域名后追加此路徑绩聘;

<a href='login/'>:沒有反斜杠,在當前url后追加此路徑沥割;

<a href='?city="wuhan"&province="hubei"'></a>:直接在后臺通過request.GET中以字典方式取出數(shù)據(jù)耗啦,{city:"wuhan;province:"hubei"},可以作為分組判斷條件机杜;

<a href='/test/1/'></a>
此處的數(shù)字 1 可以在 后臺作為參數(shù)接收.
無名分組:url(r'^test/(\d{0,4})/', views.f1),芹彬,函數(shù)接收參 f1(request, id)
命名分組:若路徑中存在多個參數(shù)叉庐,那么可以采用命名分組:url(r'^test/(?P<id>\d{4})/(?P<month>\d{2})', views.func),

<form action=''>和ajax中的url=''同理


視圖VIEW

兩種請求方式get舒帮,post

三種方法

render(request, 'index.html', {k:v,..}):從數(shù)據(jù)庫中取出數(shù)據(jù)渲染到模板,再返回給瀏覽器展示陡叠,或者直接返回模板內(nèi)容到瀏覽器.

HttpResponse(str):返回固定字符串玩郊,若為標簽文本,頁面亦可以識別.

redirect('/login/'):重新到路由配置下匹配url.

函數(shù)方式:url對應的是視圖函數(shù)

CBV模型

url對應一個視圖類.此類調(diào)用父類的dispatch()方法枉阵,會根據(jù)http請求頭里的方法是get還是post方法來執(zhí)行相應的函數(shù)译红。在觸發(fā)視圖類的時候可以在dispatch函數(shù)內(nèi)部實現(xiàn)一些邏輯判斷.

from django.views import View
from django.shortcuts import render, redirect,HttpResponse

class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        '''可以在內(nèi)部做出邏輯判斷'''
        if request.POST.get('username')=='alex':
            return HttpResponse('用戶名已注冊')
        ret = super(LoginView, self).dispatch(request, *args, **kwargs)
        return ret

    def get(self, request):
        obj = MyForm()
        return render(request, 'login.html', {'form': obj})

    def post(self, request):
        obj = MyForm(request.POST, request.FILES)
        errors = {}
        if obj.is_valid():
            values = obj.clean()
            print(values)
        else:
            errors = obj.errors
            print(errors)
        return render(request, 'login.html', {'form': obj, 'errors': errors})

弊端:每個視圖類內(nèi)部都需要定義dispatch函數(shù),造成重復代碼.


模板Template

通過模板語法渲染從數(shù)據(jù)庫中取出的數(shù)據(jù).

變量

一般是以字典形式放在上下文中:{'book', book_obj}兴溜,通過視圖邏輯render到模板中.
查詢語法:
{{ iterable.0 }}:若后端傳入的集合為列表或者元祖侦厚,那么可以根據(jù)索引取出元素;
{{ dict.key}}:若為字典拙徽;
{{ book.index.author}}:若為類對象.

標簽

語法{% %}

循環(huán):在前端遍歷對象集合并取出屬性值

{% for person in person_list %}
    <p>{{person.name}}</p>
{% empty %)  // 判斷是否為空刨沦,內(nèi)置標簽判斷
    <p>沒有符合的結(jié)果</p>
{%  endfor %}

分支判斷:

{% if i > 100 %}
    <p>100</p>
{% else %}
    <p>{{i}}</p>
{% endif %}

{% with %}:使用一個簡單地名字緩存一個復雜的變量,當你需要使用一個“昂貴的”方法(比如訪問數(shù)據(jù)庫)很多次的時候是非常有用的

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

當前循環(huán)計數(shù){{forloop.counter}}膘怕;

內(nèi)置過濾器

取值
{{html_str|first}}:返回value的第一個元素, 對有序集合和列表都適用.
{{html_str|last}}:同first相反.
{{ value|get_digit:"2" }}:value為數(shù)字想诅,返回從右往左數(shù)第2個數(shù)字.

修改
{{html_str|slice:10}}:返回前10個元素.
{{str|truncatechars:'int'}}:截斷字符,超過部分用...取代.


{{value|add: arg}}:將arg添加至value中。若value為4岛心,argu為2来破,則輸出6;若value為[1,2,3], argu為[4,5,6]忘古,則結(jié)果為[1,2,3,4,5,6].


{{value|cut: arg}}:刪除value中所有的argu徘禁。若value為'hello world' argu為 '',則輸出 helloworld.

功能
length:返回value的長度.
capfirst: 首字母大寫.
lower:小寫.
make_list:將value轉(zhuǎn)換為list,例如value為‘123’,則輸出[1,2,3]
random 從value中隨 機挑選一個元素并返回
{{html_str|safe}}:保證頁面的安全性芋膘,默認將變量內(nèi)容轉(zhuǎn)換成純文本顯示.加上safe,確定你的數(shù)據(jù)是安全的才能被當成是標簽
{{ datetime_instance|date:"Y-m-j G:i" }}:顯示結(jié)果2016-8-8 8:08

自定義過濾器

配置
1.在settings.py中的INSTALLED_APPS配置當前app骤菠,不然django無法找到自定義的simple_tag
2.在對應的app應用目錄下創(chuàng)建templatetags模塊(模塊名只能是templatetags)疤孕;
3.在templatetags里面創(chuàng)建任意my_tags.py文件商乎;

無參數(shù)

# 1. 生成注冊類
from django import template
register = template.Library()

# 2. 定義過濾器并注冊
@register.filter
def square(value):
    try:
        return int(value)**2
    except:
        return '輸入必須為數(shù)字'    

有參數(shù){{ html_str|add:"11" }},只能傳遞一個參數(shù).

@register.filter
def add(value, arg):
    try:
        return int(value)+int(arg)*10
    except:
        return '輸入必須為數(shù)字'

在模板中使用{% load 'filename' %}標簽裝載自定義標簽或者裝飾器.

自定義標簽

在后端可以先在后臺自定義標簽中將要展現(xiàn)的內(nèi)容制作成標簽字符串祭阀,在前端直接調(diào)用標簽語言就可以呈現(xiàn)內(nèi)容.

from django import template
from django.utils.safestring import mark_safe

register = template.Library()

@register.simple_tag
def get_html(s1, s2):
    html = mark_safe('<h1>標題1{0}</h1><h2>標題2{1}</h2>'.format(s1, s2))
    return html

# 在前端直接引用
{% load my_tag%}

{% tag_name arg1 arg2 %}
模板繼承

{% extends 'base.html' %}必須放在首行.

覆蓋父模板盒子中的內(nèi)容:{% block name %} 重新定制的代碼 {% endblock %}鹉戚,盒子越多越好鲜戒,靈活性更好.

{% include "test.html" %}:直接在當前模板引入文件test.html的內(nèi)容;
取到父模板盒子的內(nèi)容,然后再對其追加內(nèi)容:{{ block.super }}

Model

以類的方式創(chuàng)建和保存數(shù)據(jù).

具體步驟
1.在終端創(chuàng)建數(shù)據(jù)庫db_pro抹凳;
2.在setting中配置連接數(shù)據(jù):

# 若使用mysql
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   # 數(shù)據(jù)庫引擎
        'NAME': 'db_pro',       # 你要存儲數(shù)據(jù)的庫名遏餐,事先要創(chuàng)建之
        'USER': 'root',         # 數(shù)據(jù)庫用戶名
        'PASSWORD': '...',      # 密碼
        'HOST': 'localhost',    # 主機
        'PORT': '3306',         # 數(shù)據(jù)庫使用的端口
    }
}   

3.在app的init文件中配置,本身就是通過pymysql對數(shù)據(jù)庫進行操作:

# 先安裝pymysql模塊赢底,pip install pymysql
import pymysql
pymysql.install_as_MySQLdb()

4. 在model中創(chuàng)建模型
5.數(shù)據(jù)遷移失都,若需要使用后臺添加數(shù)據(jù)則需要創(chuàng)建超級用戶:python manage.py createsuperuser

python manage.py makemigrations
python manage.py migrate

6.生成表
django自帶的表:session表,auth權(quán)限表幸冻,遷移記錄表
model用戶新建的表:app_class_name

在model中自定義錯誤信息

創(chuàng)建表結(jié)構(gòu)

自定義model類.
常用model字段

# 示例
class Course(models.Model):
    name = models.CharField(max_length=50, verbose_name='課程名')
    detail = models.TextField(verbose_name='課程詳情')
    degree = models.CharField(choices=(('CJ', '初級'), ('ZJ', '中級'), ('GJ', '高級')), max_length=10)
    learn_times = models.IntegerField(default=0, verbose_name='學習時長(分鐘)')
    org = models.ForeignKey(CourseOrg, verbose_name="所屬課程機構(gòu)")
    image = models.ImageField(upload_to="course/%Y/%m",verbose_name='課程圖片', max_length=100)
    add_time = models.DateTimeField(default=datetime.now,verbose_name='課程添加時間')
    recommend = models.BooleanField(default=False,verbose_name='是否推薦',max_length=2)
    
FileField
EmailField

model中的字段類型自帶檢測功能粹庞,不滿足條件時候會報錯.在admin添加記錄時候自動截斷,例如限制最大長度洽损,那么只取最大長度的字符串.
表關系同mysql數(shù)據(jù)庫

admin相關

可以在admin后臺對表進行記錄操作.
注冊model:必須在app下的admin.py注冊model的類后才能在后臺顯示.

from django.contrib import admin
from app.models import Book

admin.site.register(Book)

修改顯示表名

def __str__:    
    return self.name

修改顯示的字段名verbose_name = ''

定義顯示的字段:

# admin.py
from django.contrib import admin
from blog.models import Blog
  
#Blog模型的管理器
class BlogAdmin(admin.ModelAdmin):
    list_display=('id', 'caption', 'author', 'publish_time')
     
#在admin中注冊綁定
admin.site.register(Blog, BlogAdmin)

修改表名:

    class Meta:
        db_table = 'new_name'
表記錄操作

增加記錄:實例化并賦值, 隨后save();

# 方法1
book = Book(**kwargs)
book.save()

# 方法2
Book.create(**kwargs)

刪記錄Book.objects.filter(title='').delete()
改記錄

# 方法1:
Book.objects.filter(title='t1').update(title='t2')

# 方法2:
b = Book.objects.get('')
b.title = ''
b.save()
單表查詢

ORM操作主要是查詢操作.

# 只針對int類型字段大小比較
models.Tb1.objects.filter(id__gt=1)              # 獲取id大于1的值
models.Tb1.objects.filter(id__gte=1)             # 獲取id大于等于1的值
models.Tb1.objects.filter(id__lt=10)             # 獲取id小于10的值
models.Tb1.objects.filter(id__lte=10)            # 獲取id小于10的值
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大于1 且 小于10的值

# in范圍比較
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等于11庞溜、22、33的數(shù)據(jù)
# not in
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
# range
models.Tb1.objects.filter(id__range=[1, 2])   # 范圍bettwen and

# isnull
Entry.objects.filter(pub_date__isnull=True)

# 其他類似
startswith碑定,istartswith, endswith, iendswith,
# contains包含
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
models.Tb1.objects.exclude(name__icontains="ven")
# regex正則匹配流码,iregex 不區(qū)分大小寫
Entry.objects.get(title__regex=r'^(An?|The) +')
Entry.objects.get(title__iregex=r'^(an?|the) +')

# 升序
orderby('id')  
orderby('-id')  # 降序列
# 切片
models.Tb1.objects.all()[10:20]

.get()  # 有就返回首個,沒有報錯;
.only('id', 'title')    # 只取某些字段
基于對象的查詢
# 查詢linux這本書的出版社的地址
book_obj = Book.objects.filter(title="linux").first()
addr = book_obj.publisher.address

# 查詢這本書的所有作者
book_obj = Book.objects.filter(title="蔣勛說唐詩").first()
authors = book_obj.author.all()
基于雙下劃線的跨表查詢

通過表之間的外鍵產(chǎn)生聯(lián)系才能連表查詢。以下練習是基于書|出版社|作者三張表延刘。主要分為正向和反向查詢兩種思維方式漫试。
分組annotate

ret = Book.objects.values('category_title').annotate(count=Count('id')
    avg_price = Avg('price')
)
結(jié)果 <QuerySet [{'count': 3, 'avg_price': 59.0, 'category__title': '歷史'}, 
{'count': 2, 'avg_price': 55.0, 'category__title': '小說'}]>

聚合函數(shù)aggregate

ret = Book.objects.aggregate(count=Count('id'), avg_price= Avg('price'))
結(jié)果 {'avg_price': 57.4, 'count': 5}

values('id', 'title'):每一行記錄為一個字典,整體是list列表访娶,
例如[{'id':1,'title':'asa'},...].

value_list('id','title'):每一行記錄的為一個元祖,整體是list列表.
例如[(1,'asas'),...]

練習1.查詢linux這本書的出版社地址:

# 方法1. 先定位book表,  在values中聯(lián)表查詢
addr = Book.objects.filter(title="linux").values("publisher__name","publisher__address")
print(addr)

# 方法2. 先定位出版社, 在filter中聯(lián)表查詢
result = Publisher.objects.filter(book__title="linux").values("address", "name")
print(result)

聯(lián)系2.查詢"蔣勛說唐詩"這本書的所有作者的名字:

# 方法1: 正向查詢,先找出書,隨后連表查詢關聯(lián)的作者
authors = Book.objects.filter(title="蔣勛說唐詩").values("author__name")
print(authors)

# 方法2:先找出作者,在過濾中連表篩選
res = Author.objects.filter(book__title="蔣勛說唐詩").values("name")
print(res)

練習3.查詢價格大于40的書籍的作者姓名

# 方法1:正向查詢, 從書入手
res = Book.objects.filter(price__gt=40).values("author__name").distinct()

# 反向查詢,從作者入手
res = Author.objects.filter(book__price__gt=40).values("book__title").distinct()
聚合查詢aggregate()

作用對象是整個query_set集合或者一個列表商虐,返回一個字典: {要計算的字段名: 函數(shù)返回的結(jié)果, ..},此方法需要配合計算函數(shù)使用: Avg, Sum, Count, Max, Min
練習1:查詢所有圖書的平均價格

from django.db.models import Avg, Sum,Count,Max,Min
ret = models.Book.objects.all().aggregate(Avg('price'), Sum('price'))

mysql> select avg(price), sum(price) from booksys_book;
結(jié)果: {'price__avg': 70.63636363636364, 'price__sum': 777}

練習2: 查詢"人民出版社"出版的最貴的書

res = Publisher.objects.get(name="人民出版社").book_set.values("price",'title').aggregate(Max("price"))

結(jié)果: {'price__max': 111}

// 多個字段一樣查詢
models.Publisher.objects.get(id=1).books.values('title', 'price').aggregate(Max('price'), Sum('price'))
{'price__max': 111, 'price__sum': 400}
分組查詢annotate()

作用對象是每個分組, 可以通過values()指定分組的字段;

  1. 返回的是query_set集合的鍵值對, 包含分組字段和查詢字段以及對應的值;
  2. 此方法需要配合計算函數(shù)使用: Avg, Sum, Count, Max, Min

練習1: 查詢每一個作者出版過書的最高價格

# values相當于對書籍分類進行分組崖疤,隨后再利用annotate對每個分組用聚合函數(shù)
res = Book.objects.values("type").annotate(Max("price")))

<QuerySet [{'type': '1', 'price__max': 66}, {'type': '10', 'price__max': 23}, {'type': '2', 'price__max': 33}, {'type': '3', 'price__max': 200}]>

練習2: 每個出版社出版過的最高價格的書

models.Book.objects.values('publisher').distinct().annotate(Max('price'))

<QuerySet [{'publisher': 1, 'price__max': 111}, {'publisher': 2, 'price__max': 200}]>
F查詢

可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值典勇。

// 練習1: 查看評論數(shù)大于閱讀數(shù)的書
models.Book.objects.filter(comment_num=F('read_num'))

// 練習2: 給每本書漲價20元
mysql> update booksys_book set price+=10;
# 貌似每次最大遞增有限制
Book.objects.all().update(price=F("price")+20)
Q查詢

支持的邏輯運算符: 與&, 或|, 非~

# 查詢以"蔣"開頭且價格大于50的書
book = Book.objects.filter(title__startswith="蔣", price__gt=50).values('title', "price")

# 查詢以"鋼"開頭或價格大于100的所有書
models.Book.objects.filter(Q(title__startswith='鋼')|Q(price__gt=100)).all()
查看數(shù)據(jù)庫的sql語句(加在settings.py)
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

Django內(nèi)置功能

信號

內(nèi)置信號

pre_init            # Django中的model對象執(zhí)行其構(gòu)造方法前,自動觸發(fā)
post_init           # Django中的model對象執(zhí)行其構(gòu)造方法后,自動觸發(fā)
pre_save            # Django中的model對象保存前,自動觸發(fā)
post_save           # Django中的model對象保存后,自動觸發(fā)
pre_delete          # Django中的model對象刪除前,自動觸發(fā)
post_delete         # Django中的model對象刪除后,自動觸發(fā)
m2m_changed         #Django中的model對象使用m2m字段操作數(shù)據(jù)庫的第三張表(add,remove,clear,update),自動觸發(fā)
class_prepared    # 程序啟動時,檢測到已注冊的model類,對于每一個類,自動觸發(fā)
request_started     # 請求到來前,自動觸發(fā)
request_finished    # 請求結(jié)束后,自動觸發(fā)
got_request_exception           # 請求異常時,自動觸發(fā)
setting_changed     # 配置文件改變時,自動觸發(fā)
template_rendered   # 模板執(zhí)行渲染操作時,自動觸發(fā)
connection_created  # 創(chuàng)建數(shù)據(jù)庫連接時,自動觸發(fā)

內(nèi)置信號使用步驟
1.自定義功能函數(shù)劫哼,并且將其綁定到內(nèi)置信號上:

// 項目啟動目錄init.py

#導入需要引用的內(nèi)置信號
from django.db.models.signals import pre_save, post_save 

def pre_save_func(sender, **kwargs):
    print('pre_save_fun')
    print('pre_save_msg:', sender, kwargs)

def post_save_func(sender, **kwargs):
    print('post_save_func')
    print('post_save_msg:', sender,kwargs)

pre_save.connect(pre_save_func)  # 綁定功能函數(shù)到信號
post_save.connect(post_save_func)


# 打印結(jié)果
sender:  <class 'app01.models.UserInfo'>  # model類
參數(shù)(鍵值對): {'signal': <django.db.models.signals.ModelSignal object at 0x0000000002E5F0B8>, 'instance': <UserInfo: UserInfo object>, '
created': True, 'update_fields': None, 'raw': False, 'using': 'default'
}

自定義信號
1.在項目根目錄下創(chuàng)建signal_test.py文件, 定義信號

import django.dispatch
action=django.dispatch.Signal(providing_args=["aaa","bbb"])

2.還是在項目應用的init文件中完成功能函數(shù)和綁定信號

from signal_test import action

def pre_save_func(sender, **kwargs):
    print("pre_save_func")
    print("pre_save_msg:", sender, kwargs)

action.connect(pre_save_func)

3.在視圖函數(shù)中觸發(fā)信號

def add_user(request):
    UserInfo.objects.create(name='alex', age=12)
    action.send(sender='python', aaa=111, bbb=222)
    return HttpResponse('添加成功')

4.結(jié)果展示

sender:python 
參數(shù): {'signal': <django.dispatch.dispatcher.Signal object at 0x0000000003CCC5F8>, 'aaa': 111, 'bbb': 222}

信號和中間件的區(qū)別:信號比中間件散布的范圍更廣。在后臺的許多動作事件都可以觸發(fā)內(nèi)置的信號割笙,從而執(zhí)行與之綁定的函數(shù)权烧,也可以自定義信號擴展其功能;而中間件常用的就只有四個函數(shù)伤溉,且僅在接受請求和返回請求的過程中起作用般码。

緩存

由于Django是動態(tài)網(wǎng)站,所有每次請求可能進行數(shù)據(jù)庫相關操作乱顾,當程序訪問量大時板祝,耗時必然會更加明顯,最簡單解決方式是使用緩存走净。緩存將一個某個views的返回值保存至內(nèi)存或者memcache中券时,在有效時間內(nèi)不再去執(zhí)行view中的操作孤里,而是直接從內(nèi)存或者Redis中之前緩存的內(nèi)容拿到并返回。

[圖片上傳失敗...(image-46f969-1530180354286)]

按照存儲位置的六種緩存方式
1.內(nèi)存中緩存: 內(nèi)容以字符串形式存儲在內(nèi)存中, 但是位置無法確定!

# setting.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
        'TIMEOUT': 300,  # 緩存超時時間(默認300橘洞,None表示永不過期捌袜,0表示立即過期)
        'OPTIONS': {
            'MAX_ENTRIES': 300,  # 最大緩存?zhèn)€數(shù)(默認300)
            'CULL_FREQUENCY': 3,  # 緩存到達最大個數(shù)之后,剔除緩存?zhèn)€數(shù)的比例炸枣,即:1/CULL_FREQUENCY(默認3)
        },

    }
}

2虏等,文件中緩存: 保存至指定的文件

CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache', # 需要指定文件夾路徑
        }
    }
# 注:其他配置同開發(fā)調(diào)試版本

3,數(shù)據(jù)庫中緩存

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table', # 數(shù)據(jù)庫表
    }
}

# 注:執(zhí)行創(chuàng)建表命令 python manage.py createcachetable

4适肠,Memcache緩存(python-memcached模塊)

CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',  # 類似于連接遠程緩存服務器
        }
    }
    
     CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
                'LOCATION': [
                    # # 支持簡單的分布式,將數(shù)據(jù)分別存儲在多個內(nèi)存中, 防止其中一個緩存清空
                    '172.19.26.240:11211',  
                    '172.19.26.242:11211',
                ]
            }
        }
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
}   

按照緩存的使用范圍分類
1霍衫,全棧緩存
url經(jīng)過一系列的認證等操作,如果請求的內(nèi)容在緩存中存在迂猴,則使用FetchFromCacheMiddleware 獲取內(nèi)容并返回給用戶慕淡,當返回給用戶之前,判斷緩存中是否已經(jīng)存在沸毁,如果不存在則UpdateCacheMiddleware會將緩存保存至緩存峰髓,從而實現(xiàn)全站緩存。

# setting.py配置
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 中間放其他中間件
'django.middleware.cache.FetchFromCacheMiddleware',
]

CACHE_MIDDLEWARE_SECONDS = 5  # 每隔5s更新數(shù)據(jù)


# views.py
# 可以模擬每次訪問, 看是否user信息是否更新, 結(jié)果是每5s一次更新一條數(shù)據(jù), 說明前4s的所有請求都沒有到views視圖函數(shù)中,直接從緩存中拿取數(shù)據(jù)
def add_user(request):
    UserInfo.objects.create(name='alex', age=12)
    user_list = UserInfo.objects.all()  # 在有效時間內(nèi)有限從緩存中取數(shù)據(jù)息尺,所以不會及時更新
    return render(request, 'index.html', {
        'user_list': user_list
    })

2携兵,單獨視圖實現(xiàn)緩存,例如某些靜態(tài)頁面可以使用此方法

Bв 方式一:
from django.views.decorators.cache import cache_page

# 參數(shù)為更新的時間(s)
@cache_page(60 * 15)
def my_view(request):
    ...

P旖簦 方式二:
from django.views.decorators.cache import cache_page

urlpatterns = [
    url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]

3,頁面的局部更新炭懊,例如購物網(wǎng)站中商品信息和價格都需要要實時更新并级,但是剩余數(shù)量一般都需要實時更新。
利用模板局部緩存時候侮腹,請求會進行數(shù)據(jù)庫相關操作嘲碧,唯一不同的是,在更新時間內(nèi)不會將數(shù)據(jù)渲染到模板中而已。

<!DOCTYPE html>
# 在模板中導入內(nèi)置的中間件
{% load cache %}

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主頁面</title>
</head>
<body>
    <h1>主界面</h1>

    {% cache 3 '局部刷新' %}
        {% for user in user_list %}
            {{ user.name }}
        {% endfor %}
    {% endcache %}

</body>
</html>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末父阻,一起剝皮案震驚了整個濱河市愈涩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌加矛,老刑警劉巖履婉,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異斟览,居然都是意外死亡毁腿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狸棍,“玉大人身害,你說我怎么就攤上這事〔莞辏” “怎么了塌鸯?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唐片。 經(jīng)常有香客問我丙猬,道長,這世上最難降的妖魔是什么费韭? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任茧球,我火速辦了婚禮,結(jié)果婚禮上星持,老公的妹妹穿的比我還像新娘抢埋。我一直安慰自己,他們只是感情好督暂,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布揪垄。 她就那樣靜靜地躺著,像睡著了一般逻翁。 火紅的嫁衣襯著肌膚如雪饥努。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天八回,我揣著相機與錄音酷愧,去河邊找鬼。 笑死缠诅,一個胖子當著我的面吹牛溶浴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播管引,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼戳葵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汉匙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤生蚁,失蹤者是張志新(化名)和其女友劉穎噩翠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邦投,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡伤锚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了志衣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯援。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡猛们,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狞洋,到底是詐尸還是另有隱情弯淘,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布吉懊,位于F島的核電站庐橙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏借嗽。R本人自食惡果不足惜态鳖,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恶导。 院中可真熱鬧浆竭,春花似錦、人聲如沸惨寿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缤沦。三九已至虎韵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缸废,已是汗流浹背包蓝。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留企量,地道東北人测萎。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像届巩,于是被迫代替她去往敵國和親硅瞧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容