python之django

歡迎學習django課程

MVC

  • 大部分開發語言中都有MVC框架
  • MVC框架的核心思想是:解耦
  • 下降各功能模塊之間的耦合性,方便變動,更容易重構代碼,最大程度上實現代碼的重用
  • m表示model,主要用於對數據庫層的封裝
  • v表示view,用於向用戶展現結果
  • c表示controller,是核心,用於處理請求、獲取數據、返回結果

MVT

  • Django是一款python的web開發框架
  • 與MVC有所不一樣,屬於MVT框架
  • m表示model,負責與數據庫交互
  • v表示view,是核心,負責接收請求、獲取數據、返回結果
  • t表示template,負責呈現內容到瀏覽器

簡介

  • 經過簡單示例,使用django完成基本流程的開發,學習django的主要的知識點,在後續課程中會逐個知識點進行深刻講解
  • 以「圖書-英雄」管理爲示例

主要知識點介紹

  • 環境搭建
  • 定義模型
  • 使用後臺管理
  • 編寫視圖
  • 定義模板

 

建立虛擬環境

  • 建立:mkvirtualenv [虛擬環境名稱]
  • 刪除:rmvirtualenv [虛擬環境名稱]
  • 進入:workon [虛擬環境名稱]
  • 退出:deactivate
  • 全部的虛擬環境,都位於/home/.virtualenvs目錄下
  • 進入虛擬環境前的提示:

 

  • 進入虛擬環境後的提示:

  • 查看當前的全部虛擬環境:workon [兩次tab鍵]
  • 查看虛擬環境中已經安裝的包
pip list
pip freeze

安裝django

  • 建議安裝1.8.2版本,這是一個穩定性高、使用廣、文檔多的版本
pip install django==1.8.2
  • 查看版本:進入python shell,運行以下代碼
import django
django.get_version()
  • 說明:使用pip install django命令進行安裝時,會自動刪除舊版本,再安裝新版本

建立項目

  • 命令django-admin startproject test1
  • 進入test1目錄,目錄結構以下圖:

 

目錄說明

 

  • manage.py:一個命令行工具,可使你用多種方式對Django項目進行交互
  • 內層的目錄:項目的真正的Python包
  • _init _.py:一個空文件,它告訴Python這個目錄應該被看作一個Python包
  • settings.py:項目的配置
  • urls.py:項目的URL聲明
  • wsgi.py:項目與WSGI兼容的Web服務器入口

設計介紹

  • 本示例完成「圖書-英雄」信息的維護,須要存儲兩種數據:圖書、英雄
  • 圖書表結構設計:
    • 表名:BookInfo
    • 圖書名稱:btitle
    • 圖書發佈時間:bpub_date
  • 英雄表結構設計:
    • 表名:HeroInfo
    • 英雄姓名:hname
    • 英雄性別:hgender
    • 英雄簡介:hcontent
    • 所屬圖書:hbook
  • 圖書-英雄的關係爲一對多

數據庫配置

  • 在settings.py文件中,經過DATABASES項進行數據庫設置
  • django支持的數據庫包括:sqlite、mysql等主流數據庫
  • Django默認使用SQLite數據庫

 

建立應用

在一個項目中能夠建立一到多個應用,每一個應用進行一種業務處理 建立應用的命令:javascript

python manage.py startapp booktest

 

  • 應用的目錄結構以下圖

定義模型類css

  • 有一個數據表,就有一個模型類與之對應
  • 打開models.py文件,定義模型類
  • 引入包from django.db import models
  • 模型類繼承自models.Model類
  • 說明:不須要定義主鍵列,在生成時會自動添加,而且值爲自動增加
  • 當輸出對象時,會調用對象的str方法
from django.db import models

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20)
    bpub_date = models.DateTimeField()
    def _ _str_ _(self):
        return "%d" % self.pk

class HeroInfo(models.Model):
    hname = models.CharField(max_length=20)
    hgender = models.BooleanField()
    hcontent = models.CharField(max_length=100)
    hBook = models.ForeignKey('BookInfo')
    def _ _str_ _(self):
        return "%d" % self.pk

生成數據表html

  • 激活模型:編輯settings.py文件,將booktest應用加入到installed_apps中

 

 

  • 生成遷移文件:根據模型類生成sql語句
python manage.py makemigrations
  • 遷移文件被生成到應用的migrations目錄

 

  • 執行遷移:執行sql語句生成數據表
python manage.py migrate

測試數據操做java

  • 進入python shell,進行簡單的模型API練習
python manage.py shell
  • 進入shell後提示以下:

 

  • 引入須要的包:
from booktest.models import BookInfo,HeroInfo
from django.utils import timezone
from datetime import *
  • 查詢全部圖書信息:
BookInfo.objects.all()
  • 新建圖書信息:
b = BookInfo()
b.btitle="射鵰英雄傳"
b.bpub_date=datetime(year=1990,month=1,day=10)
b.save()
  • 查找圖書信息:
b=BookInfo.objects.get(pk=1)
  • 輸出圖書信息:
b
b.id
b.btitle
  • 修改圖書信息:
b.btitle=u"天龍八部"
b.save()
  • 刪除圖書信息:
b.delete()

關聯對象的操做python

  • 對於HeroInfo能夠按照上面的操做方式進行
  • 添加,注意添加關聯對象
h=HeroInfo()
h.htitle=u'郭靖'
h.hgender=True
h.hcontent=u'降龍十八掌'
h.hBook=b
h.save()
  • 得到關聯集合:返回當前book對象的全部hero
b.heroinfo_set.all()
  • 有一個HeroInfo存在,必需要有一個BookInfo對象,提供了建立關聯的數據:
h=b.heroinfo_set.create(htitle=u'黃蓉',hgender=False,hcontent=u'打狗棍法')
h

 

服務器

  • 運行以下命令能夠開啓服務器
python manage.py runserver ip:port
  • 能夠不寫ip,默認端口爲8000
  • 這是一個純python編寫的輕量級web服務器,僅在開發階段使用
  • 服務器成功啓動後,提示以下信息

 

  • 默認端口是8000,能夠修改端口
python manage.py runserver 8080
  • 打開瀏覽器,輸入網址「127.0.0.1:8000」能夠打開默認頁面
  • 若是修改文件不須要重啓服務器,若是增刪文件須要重啓服務器
  • 經過ctrl+c中止服務器

管理操做

  • 站點分爲「內容發佈」和「公共訪問」兩部分
  • 「內容發佈」的部分負責添加、修改、刪除內容,開發這些重複的功能是一件單調乏味、缺少創造力的工做。爲此,Django會根據定義的模型類徹底自動地生成管理模塊

使用django的管理mysql

  • 建立一個管理員用戶
python manage.py createsuperuser,按提示輸入用戶名、郵箱、密碼
  • 啓動服務器,經過「127.0.0.1:8000/admin」訪問,輸入上面建立的用戶名、密碼完成登陸
  • 進入管理站點,默承認以對groups、users進行管理

管理界面本地化jquery

  • 編輯settings.py文件,設置編碼、時區
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'

向admin註冊booktest的模型nginx

  • 打開booktest/admin.py文件,註冊模型
from django.contrib import admin
from models import BookInfo
admin.site.register(BookInfo)
  • 刷新管理頁面,能夠對BookInfo的數據進行增刪改查操做
  • 問題:若是在str方法中返回中文,在修改和添加時會報ascii的錯誤
  • 解決:在str()方法中,將字符串末尾添加「.encode('utf-8')」

自定義管理頁面git

  • Django提供了admin.ModelAdmin類
  • 經過定義ModelAdmin的子類,來定義模型在Admin界面的顯示方式
class QuestionAdmin(admin.ModelAdmin):
    ...
admin.site.register(Question, QuestionAdmin)

列表頁屬性程序員

  • list_display:顯示字段,能夠點擊列頭進行排序
list_display = ['pk', 'btitle', 'bpub_date']
  • list_filter:過濾字段,過濾框會出如今右側
list_filter = ['btitle']
  • search_fields:搜索字段,搜索框會出如今上側
search_fields = ['btitle']
  • list_per_page:分頁,分頁框會出如今下側
list_per_page = 10

添加、修改頁屬性

  • fields:屬性的前後順序
fields = ['bpub_date', 'btitle']
  • fieldsets:屬性分組
fieldsets = [
    ('basic',{'fields': ['btitle']}),
    ('more', {'fields': ['bpub_date']}),
]

關聯對象

  • 對於HeroInfo模型類,有兩種註冊方式

    • 方式一:與BookInfo模型類相同
    • 方式二:關聯註冊
  • 按照BookInfor的註冊方式完成HeroInfo的註冊

  • 接下來實現關聯註冊
from django.contrib import admin
from models import BookInfo,HeroInfo

class HeroInfoInline(admin.StackedInline):
    model = HeroInfo
    extra = 2

class BookInfoAdmin(admin.ModelAdmin):
    inlines = [HeroInfoInline]

admin.site.register(BookInfo, BookInfoAdmin)
  • 能夠將內嵌的方式改成表格
class HeroInfoInline(admin.TabularInline)

布爾值的顯示

  • 發佈性別的顯示不是一個直觀的結果,可使用方法進行封裝
def gender(self):
    if self.hgender:
        return '男'
    else:
        return '女'
gender.short_description = '性別'
  • 在admin註冊中使用gender代替hgender
class HeroInfoAdmin(admin.ModelAdmin):
    list_display = ['id', 'hname', 'gender', 'hcontent']

視圖

  • 在django中,視圖對WEB請求進行迴應
  • 視圖接收reqeust對象做爲第一個參數,包含了請求的信息
  • 視圖就是一個Python函數,被定義在views.py中
#coding:utf-8
from django.http import HttpResponse

def index(request):
    return HttpResponse("index")
def detail(request,id):
    return HttpResponse("detail %s" % id)
  • 定義完成視圖後,須要配置urlconf,不然沒法處理請求

URLconf

  • 在Django中,定義URLconf包括正則表達式、視圖兩部分
  • Django使用正則表達式匹配請求的URL,一旦匹配成功,則調用應用的視圖
  • 注意:只匹配路徑部分,即除去域名、參數後的字符串
  • 在test1/urls.py插入booktest,使主urlconf鏈接到booktest.urls模塊
url(r'^', include('booktest.urls')),
  • 在booktest中的urls.py中添加urlconf
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index),
    url(r'^([0-9]+)/$', views.detail),
]

 

模板

  • 模板是html頁面,能夠根據視圖中傳遞的數據填充值
  • 建立模板的目錄以下圖:

 

  • 修改settings.py文件,設置TEMPLATES的DIRS值
'DIRS': [os.path.join(BASE_DIR, 'templates')],
  • 在模板中訪問視圖傳遞的數據
{{輸出值,能夠是變量,也能夠是對象.屬性}}
{%執行代碼段%}

定義index.html模板

<!DOCTYPE html>
<html>
<head>
  <title>首頁</title>
</head>
<body>
<h1>圖書列表</h1>
<ul>
{%for book in booklist%}
<li>
  <a href="{{book.id}}">
    {{book.btitle}}
  </a>
</li>
{%endfor%}
</ul>
</body>
</html>

定義detail.html模板

  • 在模板中訪問對象成員時,都以屬性的方式訪問,即方法也不能加括號
<!DOCTYPE html>
<html>
<head>
  <title>詳細頁</title>
</head>
<body>
<h1>{{book.btitle}}</h1>
<ul>
  {%for hero in book.heroinfo_set.all%}
  <li>{{hero.hname}}---{{hero.hcontent}}</li>
  {%endfor%}
</ul>
</body>
</html>

使用模板

  • 編輯views.py文件,在方法中調用模板
from django.http import HttpResponse
from django.template import RequestContext, loader
from models import BookInfo

def index(request):
    booklist = BookInfo.objects.all()
    template = loader.get_template('booktest/index.html')
    context = RequestContext(request, {'booklist': booklist})
    return HttpResponse(template.render(context))


def detail(reqeust, id):
    book = BookInfo.objects.get(pk=id)
    template = loader.get_template('booktest/detail.html')
    context = RequestContext(reqeust, {'book': book})
    return HttpResponse(template.render(context))

去除模板的硬編碼

  • 在index.html模板中,超連接是硬編碼的,此時的請求地址爲「127.0.0.1/1/」
<a href="{{book.id}}">
  • 看以下狀況:將urlconf中詳細頁改成以下,連接就找不到了
url(r'^book/([0-9]+)/$', views.detail),
  • 此時的請求地址應該爲「127.0.0.1/book/1/」
  • 問題總結:若是在模板中地址硬編碼,未來urlconf修改後,地址將失效
  • 解決:使用命名的url設置超連接
  • 修改test1/urls.py文件,在include中設置namespace
url(r'^admin/', include(admin.site.urls, namespace='booktest')),
  • 修改booktest/urls.py文件,設置name
url(r'^book/([0-9]+)/$', views.detail, name="detail"),
  • 修改index.html模板中的連接
<a href="{%url 'booktest:detail' book.id%}">

Render簡寫

  • Django提供了函數Render()簡化視圖調用模板、構造上下文
from django.shortcuts import render
from models import BookInfo

def index(reqeust):
    booklist = BookInfo.objects.all()
    return render(reqeust, 'booktest/index.html', {'booklist': booklist})


def detail(reqeust, id):
    book = BookInfo.objects.get(pk=id)
    return render(reqeust, 'booktest/detail.html', {'book': book})

總結

  1. 安裝配置django運行的環境
  2. 編寫模型,使用簡單API與數據庫交互
  3. 使用django的後臺管理中維護數據
  4. 經過視圖接收請求,經過模型獲取數據,展現出來
  5. 調用模板完成展現

做業

  • 設計一對多關係的兩個模型類
  • 使用django後臺管理
  • 經過視圖、urlconf、模板完成數據的展現
  • 控制在2個小時以內完成

 

ORM簡介

  • MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不須要依賴於特定的數據庫,經過簡單的配置就能夠輕鬆更換數據庫
  • ORM是「對象-關係-映射」的簡稱,主要任務是:
    • 根據對象的類型生成表結構
    • 將對象、列表的操做,轉換爲sql語句
    • 將sql查詢到的結果轉換爲對象、列表
  • 這極大的減輕了開發人員的工做量,不須要面對因數據庫變動而致使的無效勞動
  • Django中的模型包含存儲數據的字段和約束,對應着數據庫中惟一的表

 

使用MySql數據庫

  • 在虛擬環境中安裝mysql包
pip install mysql-python
  • 在mysql中建立數據庫
create databases test2 charset=utf8
  • 打開settings.py文件,修改DATABASES項
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test2',
        'USER': '用戶名',
        'PASSWORD': '密碼',
        'HOST': '數據庫服務器ip,本地可使用localhost',
        'PORT': '端口,默認爲3306',
    }
}

開發流程

  1. 在models.py中定義模型類,要求繼承自models.Model
  2. 把應用加入settings.py文件的installed_app項
  3. 生成遷移文件
  4. 執行遷移生成表
  5. 使用模型類進行crud操做

使用數據庫生成模型類

python manage.py inspectdb > booktest/models.py

 

定義模型

  • 在模型中定義屬性,會生成表中的字段
  • django根據屬性的類型肯定如下信息:
    • 當前選擇的數據庫支持字段的類型
    • 渲染管理表單時使用的默認html控件
    • 在管理站點最低限度的驗證
  • django會爲表增長自動增加的主鍵列,每一個模型只能有一個主鍵列,若是使用選項設置某屬性爲主鍵列後,則django不會再生成默認的主鍵列
  • 屬性命名限制
    • 不能是python的保留關鍵字
    • 因爲django的查詢方式,不容許使用連續的下劃線

定義屬性

  • 定義屬性時,須要字段類型
  • 字段類型被定義在django.db.models.fields目錄下,爲了方便使用,被導入到django.db.models中
  • 使用方式
    1. 導入from django.db import models
    2. 經過models.Field建立字段類型的對象,賦值給屬性
  • 對於重要數據都作邏輯刪除,不作物理刪除,實現方法是定義isDelete屬性,類型爲BooleanField,默認值爲False

字段類型

  • AutoField:一個根據實際ID自動增加的IntegerField,一般不指定
    • 若是不指定,一個主鍵字段將自動添加到模型中
  • BooleanField:true/false 字段,此字段的默認表單控制是CheckboxInput
  • NullBooleanField:支持null、true、false三種值
  • CharField(max_length=字符長度):字符串,默認的表單樣式是 TextInput
  • TextField:大文本字段,通常超過4000使用,默認的表單控件是Textarea
  • IntegerField:整數
  • DecimalField(max_digits=None, decimal_places=None):使用python的Decimal實例表示的十進制浮點數
    • DecimalField.max_digits:位數總數
    • DecimalField.decimal_places:小數點後的數字位數
  • FloatField:用Python的float實例來表示的浮點數
  • DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date實例表示的日期
    • 參數DateField.auto_now:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它老是使用當前日期,默認爲false
    • 參數DateField.auto_now_add:當對象第一次被建立時自動設置當前時間,用於建立的時間戳,它老是使用當前日期,默認爲false
    • 該字段默認對應的表單控件是一個TextInput. 在管理員站點添加了一個JavaScript寫的日曆控件,和一個「Today"的快捷按鈕,包含了一個額外的invalid_date錯誤消息鍵
    • auto_now_add, auto_now, and default 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
  • TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
  • DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
  • FileField:一個上傳文件的字段
  • ImageField:繼承了FileField的全部屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image

字段選項

  • 經過字段選項,能夠實現對字段的約束
  • 在字段對象時經過關鍵字參數指定
  • null:若是爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
  • blank:若是爲True,則該字段容許爲空白,默認值是 False
  • 對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
  • db_column:字段的名稱,若是未指定,則使用屬性的名稱
  • db_index:若值爲 True, 則在表中會爲此字段建立索引
  • default:默認值
  • primary_key:若爲 True, 則該字段會成爲模型的主鍵字段
  • unique:若是爲 True, 這個字段在表中必須有惟一值

關係

  • 關係的類型包括
    • ForeignKey:一對多,將字段定義在多的端中
    • ManyToManyField:多對多,將字段定義在兩端中
    • OneToOneField:一對一,將字段定義在任意一端中
  • 能夠維護遞歸的關聯關係,使用'self'指定,詳見「自關聯」
  • 用一訪問多:對象.模型類小寫_set
bookinfo.heroinfo_set
  • 用一訪問一:對象.模型類小寫
heroinfo.bookinfo
  • 訪問id:對象.屬性_id
heroinfo.book_id

元選項

  • 在模型類中定義類Meta,用於設置元信息
  • 元信息db_table:定義數據表名稱,推薦使用小寫字母,數據表的默認名稱
<app_name>_<model_name>
  • ordering:對象的默認排序字段,獲取對象的列表時使用,接收屬性構成的列表
class BookInfo(models.Model):
    ...
    class Meta():
        ordering = ['id']
  • 字符串前加-表示倒序,不加-表示正序
class BookInfo(models.Model):
    ...
    class Meta():
        ordering = ['-id']
  • 排序會增長數據庫的開銷

示例演示

  • 建立test2項目,並建立booktest應用,使用mysql數據庫
  • 定義圖書模型
class BookInfo(models.Model):
    btitle = models.CharField(max_length=20)
    bpub_date = models.DateTimeField()
    bread = models.IntegerField(default=0)
    bcommet = models.IntegerField(default=0)
    isDelete = models.BooleanField(default=False)
  • 英雄模型
class HeroInfo(models.Model):
    hname = models.CharField(max_length=20)
    hgender = models.BooleanField(default=True)
    isDelete = models.BooleanField(default=False)
    hcontent = models.CharField(max_length=100)
    hbook = models.ForeignKey('BookInfo')
  • 定義index、detail視圖
  • index.html、detail.html模板
  • 配置url,可以完成圖書及英雄的展現

測試數據

  • 模型BookInfo的測試數據
insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) values
('射鵰英雄傳','1980-5-1',12,34,0),
('天龍八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飛狐','1987-11-11',58,24,0)
  • 模型HeroInfo的測試數據
insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) values
('郭靖',1,1,'降龍十八掌',0),
('黃蓉',0,1,'打狗棍法',0),
('黃藥師',1,1,'彈指神通',0),
('歐陽鋒',1,1,'蛤蟆功',0),
('梅超風',0,1,'九陰白骨爪',0),
('喬峯',1,2,'降龍十八掌',0),
('段譽',1,2,'六脈神劍',0),
('虛竹',1,2,'天山六陽掌',0),
('王語嫣',0,2,'神仙姐姐',0),
('令狐沖',1,3,'獨孤九劍',0),
('任盈盈',0,3,'彈琴',0),
('嶽不羣',1,3,'華山劍法',0),
('東方不敗',0,3,'葵花寶典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若蘭',0,4,'黃衣',0),
('程靈素',0,4,'醫術',0),
('袁紫衣',0,4,'六合拳',0)

類的屬性

  • objects:是Manager類型的對象,用於與數據庫進行交互
  • 當定義模型類時沒有指定管理器,則Django會爲模型類提供一個名爲objects的管理器
  • 支持明確指定模型類的管理器
class BookInfo(models.Model):
    ...
    books = models.Manager()
  • 當爲模型類指定管理器後,django再也不爲模型類生成名爲objects的默認管理器

管理器Manager

  • 管理器是Django的模型進行數據庫的查詢操做的接口,Django應用的每一個模型都擁有至少一個管理器
  • 自定義管理器類主要用於兩種狀況
  • 狀況一:向管理器類中添加額外的方法:見下面「建立對象」中的方式二
  • 狀況二:修改管理器返回的原始查詢集:重寫get_queryset()方法
class BookInfoManager(models.Manager):
    def get_queryset(self):
        return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
    ...
    books = BookInfoManager()

建立對象

  • 當建立對象時,django不會對數據庫進行讀寫操做
  • 調用save()方法才與數據庫交互,將對象保存到數據庫中
  • 使用關鍵字參數構造模型對象很麻煩,推薦使用下面的兩種之式
  • 說明: _init _方法已經在基類models.Model中使用,在自定義模型中沒法使用,
  • 方式一:在模型類中增長一個類方法
class BookInfo(models.Model):
    ...
    @classmethod
    def create(cls, title, pub_date):
        book = cls(btitle=title, bpub_date=pub_date)
        book.bread=0
        book.bcommet=0
        book.isDelete = False
        return book
引入時間包:from datetime import *
調用:book=BookInfo.create("hello",datetime(1980,10,11));
保存:book.save()
  • 方式二:在自定義管理器中添加一個方法
  • 在管理器的方法中,能夠經過self.model來獲得它所屬的模型類
class BookInfoManager(models.Manager):
    def create_book(self, title, pub_date):
        book = self.model()
        book.btitle = title
        book.bpub_date = pub_date
        book.bread=0
        book.bcommet=0
        book.isDelete = False
        return book

class BookInfo(models.Model):
    ...
    books = BookInfoManager()
調用:book=BookInfo.books.create_book("abc",datetime(1980,1,1))
保存:book.save()
  • 在方式二中,能夠調用self.create()建立並保存對象,不須要再手動save()
class BookInfoManager(models.Manager):
    def create_book(self, title, pub_date):
        book = self.create(btitle = title,bpub_date = pub_date,bread=0,bcommet=0,isDelete = False)
        return book

class BookInfo(models.Model):
    ...
    books = BookInfoManager()
調用:book=Book.books.create_book("abc",datetime(1980,1,1))
查看:book.pk

實例的屬性

  • DoesNotExist:在進行單個查詢時,模型的對象不存在時會引起此異常,結合try/except使用

實例的方法

  • str (self):重寫object方法,此方法在將對象轉換成字符串時會被調用
  • save():將模型對象保存到數據表中
  • delete():將模型對象從數據表中刪除

 

簡介

  • 查詢集表示從數據庫中獲取的對象集合
  • 查詢集能夠含有零個、一個或多個過濾器
  • 過濾器基於所給的參數限制查詢的結果
  • 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
  • 接下來主要討論以下知識點
    • 查詢集
    • 字段查詢:比較運算符,F對象,Q對象

查詢集

  • 在管理器上調用過濾器方法會返回查詢集
  • 查詢集通過過濾器篩選後返回新的查詢集,所以能夠寫成鏈式過濾
  • 惰性執行:建立查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫
  • 什麼時候對查詢集求值:迭代,序列化,與if合用
  • 返回查詢集的方法,稱爲過濾器
    • all()
    • filter()
    • exclude()
    • order_by()
    • values():一個對象構成一個字典,而後構成一個列表返回
  • 寫法:
filter(鍵1=值1,鍵2=值2)
等價於
filter(鍵1=值1).filter(鍵2=值2)
  • 返回單個值的方法
    • get():返回單個知足條件的對象
      • 若是未找到會引起"模型類.DoesNotExist"異常
      • 若是多條被返回,會引起"模型類.MultipleObjectsReturned"異常
    • count():返回當前查詢的總條數
    • first():返回第一個對象
    • last():返回最後一個對象
    • exists():判斷查詢集中是否有數據,若是有則返回True

限制查詢集

  • 查詢集返回列表,可使用下標的方式進行限制,等同於sql中的limit和offset子句
  • 注意:不支持負數索引
  • 使用下標後返回一個新的查詢集,不會當即執行查詢
  • 若是獲取一個對象,直接使用[0],等同於[0:1].get(),可是若是沒有數據,[0]引起IndexError異常,[0:1].get()引起DoesNotExist異常

查詢集的緩存

  • 每一個查詢集都包含一個緩存來最小化對數據庫的訪問
  • 在新建的查詢集中,緩存爲空,首次對查詢集求值時,會發生數據庫查詢,django會將查詢的結果存在查詢集的緩存中,並返回請求的結果,接下來對查詢集求值將重用緩存的結果
  • 狀況一:這構成了兩個查詢集,沒法重用緩存,每次查詢都會與數據庫進行一次交互,增長了數據庫的負載
print([e.title for e in Entry.objects.all()])
print([e.title for e in Entry.objects.all()])
  • 狀況二:兩次循環使用同一個查詢集,第二次使用緩存中的數據
querylist=Entry.objects.all()
print([e.title for e in querylist])
print([e.title for e in querylist])
  • 什麼時候查詢集不會被緩存:當只對查詢集的部分進行求值時會檢查緩存,可是若是這部分不在緩存中,那麼接下來查詢返回的記錄將不會被緩存,這意味着使用索引來限制查詢集將不會填充緩存,若是這部分數據已經被緩存,則直接使用緩存中的數據

字段查詢

  • 實現where子名,做爲方法filter()、exclude()、get()的參數
  • 語法:屬性名稱__比較運算符=值
  • 表示兩個下劃線,左側是屬性名稱,右側是比較類型
  • 對於外鍵,使用「屬性名_id」表示外鍵的原始值
  • 轉義:like語句中使用了%與,匹配數據中的%與,在過濾器中直接寫,例如:filter(title__contains="%")=>where title like '%\%%',表示查找標題中包含%的

比較運算符

  • exact:表示判等,大小寫敏感;若是沒有寫「 比較運算符」,表示判等
filter(isDelete=False)
  • contains:是否包含,大小寫敏感
exclude(btitle__contains='傳')
  • startswith、endswith:以value開頭或結尾,大小寫敏感
exclude(btitle__endswith='傳')
  • isnull、isnotnull:是否爲null
filter(btitle__isnull=False)
  • 在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
  • in:是否包含在範圍內
filter(pk__in=[1, 2, 3, 4, 5])
  • gt、gte、lt、lte:大於、大於等於、小於、小於等於
filter(id__gt=3)
  • year、month、day、week_day、hour、minute、second:對日期間類型的屬性進行運算
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
  • 跨關聯關係的查詢:處理join查詢
    • 語法:模型類名 <屬性名> <比較>
    • 注:能夠沒有__<比較>部分,表示等於,結果同inner join
    • 可返向使用,即在關聯的兩個模型中均可以使用
filter(heroinfo_ _hcontent_ _contains='八')
  • 查詢的快捷方式:pk,pk表示primary key,默認的主鍵是id
filter(pk__lt=6)

聚合函數

  • 使用aggregate()函數返回聚合函數的值
  • 函數:Avg,Count,Max,Min,Sum
from django.db.models import Max
maxDate = list.aggregate(Max('bpub_date'))
  • count的通常用法:
count = list.count()

F對象

  • 可使用模型的字段A與字段B進行比較,若是A寫在了等號的左邊,則B出如今等號的右邊,須要經過F對象構造
list.filter(bread__gte=F('bcommet'))
  • django支持對F()對象使用算數運算
list.filter(bread__gte=F('bcommet') * 2)
  • F()對象中還能夠寫做「模型類__列名」進行關聯查詢
list.filter(isDelete=F('heroinfo__isDelete'))
  • 對於date/time字段,可與timedelta()進行運算
list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))

Q對象

  • 過濾器的方法中關鍵字參數查詢,會合併爲And進行
  • 須要進行or查詢,使用Q()對象
  • Q對象(django.db.models.Q)用於封裝一組關鍵字參數,這些關鍵字參數與「比較運算符」中的相同
from django.db.models import Q
list.filter(Q(pk_ _lt=6))
  • Q對象可使用&(and)、|(or)操做符組合起來
  • 當操做符應用在兩個Q對象時,會產生一個新的Q對象
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))
  • 使用~(not)操做符在Q對象前表示取反
list.filter(~Q(pk__lt=6))
  • 可使用&|~結合括號進行分組,構造作生意複雜的Q對象
  • 過濾器函數能夠傳遞一個或多個Q對象做爲位置參數,若是有多個Q對象,這些參數的邏輯爲and
  • 過濾器函數能夠混合使用Q對象和關鍵字參數,全部參數都將and在一塊兒,Q對象必須位於關鍵字參數的前面

自鏈接

  • 對於地區信息,屬於一對多關係,使用一張表,存儲全部的信息
  • 相似的表結構還應用於分類信息,能夠實現無限級分類
  • 新建模型AreaInfo,生成遷移
class AreaInfo(models.Model):
    atitle = models.CharField(max_length=20)
    aParent = models.ForeignKey('self', null=True, blank=True)
  • 訪問關聯對象
上級對象:area.aParent
下級對象:area.areainfo_set.all()
  • 加入測試數據(在workbench中,參見「省市區mysql.txt」)
  • 在booktest/views.py中定義視圖area
from models import AreaInfo
def area(request):
    area = AreaInfo.objects.get(pk=130100)
    return render(request, 'booktest/area.html', {'area': area})
  • 定義模板area.html
<!DOCTYPE html>
<html>
<head>
    <title>地區</title>
</head>
<body>
當前地區:{{area.atitle}}
<hr/>
上級地區:{{area.aParent.atitle}}
<hr/>
下級地區:
<ul>
    { %for a in area.areainfo_set.all%}
    <li>{{a.atitle}}</li>
    { %endfor%}
</ul>
</body>
</html>
  • 在booktest/urls.py中配置一個新的urlconf
urlpatterns = [
    url(r'^area/$', views.area, name='area')
]

 

總結

  • 使用mysql數據庫
  • 定義模型,生成遷移
  • 定義管理器
  • 查詢
  • 自鏈接

做業

  • 熟練完成當天代碼
  • 上次做業中定義的模型,改成使用mysql數據庫操做
  • 對模型數據進行查詢

視圖

  • 視圖接受Web請求而且返回Web響應
  • 視圖就是一個python函數,被定義在views.py中
  • 響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤等等
  • 響應處理過程以下圖:

 

URLconf

  • 在settings.py文件中經過ROOT_URLCONF指定根級url的配置
  • urlpatterns是一個url()實例的列表
  • 一個url()對象包括:
    • 正則表達式
    • 視圖函數
    • 名稱name
  • 編寫URLconf的注意:
    • 若要從url中捕獲一個值,須要在它周圍設置一對圓括號
    • 不須要添加一個前導的反斜槓,如應該寫做'test/',而不該該寫做'/test/'
    • 每一個正則表達式前面的r表示字符串不轉義
  • 請求的url被看作是一個普通的python字符串,進行匹配時不包括get或post請求的參數及域名
http://www.itcast.cn/python/1/?i=1&p=new,只匹配「/python/1/」部分
  • 正則表達式非命名組,經過位置參數傳遞給視圖
url(r'^([0-9]+)/$', views.detail, name='detail'),
  • 正則表達式命名組,經過關鍵字參數傳遞給視圖,本例中關鍵字參數爲id
url(r'^(?P<id>[0-9]+)/$', views.detail, name='detail'),
  • 參數匹配規則:優先使用命名參數,若是沒有命名參數則使用位置參數
  • 每一個捕獲的參數都做爲一個普通的python字符串傳遞給視圖
  • 性能:urlpatterns中的每一個正則表達式在第一次訪問它們時被編譯,這使得系統至關快

包含其它的URLconfs

  • 在應用中建立urls.py文件,定義本應用中的urlconf,再在項目的settings中使用include()
from django.conf.urls import include, url
urlpatterns = [
    url(r'^', include('booktest.urls', namespace='booktest')),
]
  • 匹配過程:先與主URLconf匹配,成功後再用剩餘的部分與應用中的URLconf匹配
請求http://www.itcast.cn/booktest/1/
在sesstings.py中的配置:
url(r'^booktest/', include('booktest.urls', namespace='booktest')),
在booktest應用urls.py中的配置
url(r'^([0-9]+)/$', views.detail, name='detail'),
匹配部分是:/booktest/1/
匹配過程:在settings.py中與「booktest/」成功,再用「1/」與booktest應用的urls匹配
  • 使用include能夠去除urlconf的冗餘
  • 參數:視圖會收到來自父URLconf、當前URLconf捕獲的全部參數
  • 在include中經過namespace定義命名空間,用於反解析

URL的反向解析

  • 若是在視圖、模板中使用硬編碼的連接,在urlconf發生改變時,維護是一件很是麻煩的事情
  • 解決:在作連接時,經過指向urlconf的名稱,動態生成連接地址
  • 視圖:使用django.core.urlresolvers.reverse()函數
  • 模板:使用url模板標籤

 

定義視圖

  • 本質就是一個函數
  • 視圖的參數
    • 一個HttpRequest實例
    • 經過正則表達式組獲取的位置參數
    • 經過正則表達式組得到的關鍵字參數
  • 在應用目錄下默認有views.py文件,通常視圖都定義在這個文件中
  • 若是處理功能過多,能夠將函數定義到不一樣的py文件中
新建views1.py
#coding:utf-8
from django.http import HttpResponse
def index(request):
    return HttpResponse("你好")

在urls.py中修改配置
from . import views1
url(r'^$', views1.index, name='index'),

錯誤視圖

  • Django原生自帶幾個默認視圖用於處理HTTP錯誤

404 (page not found) 視圖

  • defaults.page_not_found(request, template_name='404.html')
  • 默認的404視圖將傳遞一個變量給模板:request_path,它是致使錯誤的URL
  • 若是Django在檢測URLconf中的每一個正則表達式後沒有找到匹配的內容也將調用404視圖
  • 若是在settings中DEBUG設置爲True,那麼將永遠不會調用404視圖,而是顯示URLconf 並帶有一些調試信息
  • 在templates中建立404.html
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
找不到了
<hr/>
{{request_path}}
</body>
</html>
  • 在settings.py中修改調試
DEBUG = False
ALLOWED_HOSTS = ['*', ]
  • 請求一個不存在的地址
http://127.0.0.1:8000/test/

500 (server error) 視圖

  • defaults.server_error(request, template_name='500.html')
  • 在視圖代碼中出現運行時錯誤
  • 默認的500視圖不會傳遞變量給500.html模板
  • 若是在settings中DEBUG設置爲True,那麼將永遠不會調用505視圖,而是顯示URLconf 並帶有一些調試信息

400 (bad request) 視圖

  • defaults.bad_request(request, template_name='400.html')
  • 錯誤來自客戶端的操做
  • 當用戶進行的操做在安全方面可疑的時候,例如篡改會話cookie

HttpReqeust對象

  • 服務器接收到http協議的請求後,會根據報文建立HttpRequest對象
  • 視圖函數的第一個參數是HttpRequest對象
  • 在django.http模塊中定義了HttpRequest對象的API

屬性

  • 下面除非特別說明,屬性都是隻讀的
  • path:一個字符串,表示請求的頁面的完整路徑,不包含域名
  • method:一個字符串,表示請求使用的HTTP方法,經常使用值包括:'GET'、'POST'
  • encoding:一個字符串,表示提交的數據的編碼方式
    • 若是爲None則表示使用瀏覽器的默認設置,通常爲utf-8
    • 這個屬性是可寫的,能夠經過修改它來修改訪問表單數據使用的編碼,接下來對屬性的任何訪問將使用新的encoding值
  • GET:一個相似於字典的對象,包含get請求方式的全部參數
  • POST:一個相似於字典的對象,包含post請求方式的全部參數
  • FILES:一個相似於字典的對象,包含全部的上傳文件
  • COOKIES:一個標準的Python字典,包含全部的cookie,鍵和值都爲字符串
  • session:一個既可讀又可寫的相似於字典的對象,表示當前的會話,只有當Django 啓用會話的支持時纔可用,詳細內容見「狀態保持」

方法

  • is_ajax():若是請求是經過XMLHttpRequest發起的,則返回True

 

QueryDict對象

  • 定義在django.http.QueryDict
  • request對象的屬性GET、POST都是QueryDict類型的對象
  • 與python字典不一樣,QueryDict類型的對象用來處理同一個鍵帶有多個值的狀況
  • 方法get():根據鍵獲取值
    • 只能獲取鍵的一個值
    • 若是一個鍵同時擁有多個值,獲取最後一個值
dict.get('鍵',default)
或簡寫爲
dict['鍵']
  • 方法getlist():根據鍵獲取值
    • 將鍵的值以列表返回,能夠獲取一個鍵的多個值
dict.getlist('鍵',default)

GET屬性

  • QueryDict類型的對象
  • 包含get請求方式的全部參數
  • 與url請求地址中的參數對應,位於?後面
  • 參數的格式是鍵值對,如key1=value1
  • 多個參數之間,使用&鏈接,如key1=value1&key2=value2
  • 鍵是開發人員定下來的,值是可變的
  • 示例以下
  • 建立視圖getTest1用於定義連接,getTest2用於接收一鍵一值,getTest3用於接收一鍵多值
def getTest1(request):
    return render(request,'booktest/getTest1.html')
def getTest2(request):
    return render(request,'booktest/getTest2.html')
def getTest3(request):
    return render(request,'booktest/getTest3.html')
  • 配置url
url(r'^getTest1/$', views.getTest1),
url(r'^getTest2/$', views.getTest2),
url(r'^getTest3/$', views.getTest3),
  • 建立getTest1.html,定義連接
<html>
<head>
    <title>Title</title>
</head>
<body>
連接1:一個鍵傳遞一個值
<a href="/getTest2/?a=1&b=2">gettest2</a><br>
連接2:一個鍵傳遞多個值
<a href="/getTest3/?a=1&a=2&b=3">gettest3</a>
</body>
</html>
  • 完善視圖getTest2的代碼
def getTest2(request):
    a=request.GET['a']
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest2.html',context)
  • 建立getTest2.html,顯示接收結果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{{ a }}<br>
b:{{ b }}
</body>
</html>
  • 完善視圖getTest3的代碼
def getTest3(request):
    a=request.GET.getlist('a')
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest3.html',context)
  • 建立getTest3.html,顯示接收結果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{% for item in a %}
{{ item }}
{% endfor %}
<br>
b:{{ b }}
</body>
</html>

 

POST屬性

  • QueryDict類型的對象
  • 包含post請求方式的全部參數
  • 與form表單中的控件對應
  • 問:表單中哪些控件會被提交?
  • 答:控件要有name屬性,則name屬性的值爲鍵,value屬性的值爲鍵,構成鍵值對提交
    • 對於checkbox控件,name屬性同樣爲一組,當控件被選中後會被提交,存在一鍵多值的狀況
  • 鍵是開發人員定下來的,值是可變的
  • 示例以下
  • 定義視圖postTest1
def postTest1(request):
    return render(request,'booktest/postTest1.html')
  • 配置url
url(r'^postTest1$',views.postTest1)
  • 建立模板postTest1.html
<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="/postTest2/">
    姓名:<input type="text" name="uname"/><br>
    密碼:<input type="password" name="upwd"/><br>
    性別:<input type="radio" name="ugender" value="1"/>男
    <input type="radio" name="ugender" value="0"/>女<br>
    愛好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石
    <input type="checkbox" name="uhobby" value="跳樓"/>跳樓
    <input type="checkbox" name="uhobby" value="喝酒"/>喝酒
    <input type="checkbox" name="uhobby" value="登山"/>登山<br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • 建立視圖postTest2接收請求的數據
def postTest2(request):
    uname=request.POST['uname']
    upwd=request.POST['upwd']
    ugender=request.POST['ugender']
    uhobby=request.POST.getlist('uhobby')
    context={'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby}
    return render(request,'booktest/postTest2.html',context)
  • 配置url
url(r'^postTest2$',views.postTest2)
  • 建立模板postTest2.html
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}<br>
{{ upwd }}<br>
{{ ugender }}<br>
{{ uhobby }}
</body>
</html>
  • 注意:使用表單提交,註釋掉settings.py中的中間件crsf

HttpResponse對象

  • 在django.http模塊中定義了HttpResponse對象的API
  • HttpRequest對象由Django自動建立,HttpResponse對象由程序員建立
  • 不調用模板,直接返回數據
#coding=utf-8
from django.http import HttpResponse

def index(request):
    return HttpResponse('你好')
  • 調用模板
from django.http import HttpResponse
from django.template import RequestContext, loader

def index(request):
    t1 = loader.get_template('polls/index.html')
    context = RequestContext(request, {'h1': 'hello'})
    return HttpResponse(t1.render(context))

屬性

  • content:表示返回的內容,字符串類型
  • charset:表示response採用的編碼字符集,字符串類型
  • status_code:響應的HTTP響應狀態碼
  • content-type:指定輸出的MIME類型

方法

  • init :使用頁內容實例化HttpResponse對象
  • write(content):以文件的方式寫
  • flush():以文件的方式輸出緩存區
  • set_cookie(key, value='', max_age=None, expires=None):設置Cookie
    • key、value都是字符串類型
    • max_age是一個整數,表示在指定秒數後過時
    • expires是一個datetime或timedelta對象,會話將在這個指定的日期/時間過時,注意datetime和timedelta值只有在使用PickleSerializer時纔可序列化
    • max_age與expires二選一
    • 若是不指定過時時間,則兩個星期後過時
from django.http import HttpResponse
from datetime import *

def index(request):
    response = HttpResponse()
    if request.COOKIES.has_key('h1'):
        response.write('<h1>' + request.COOKIES['h1'] + '</h1>')
    response.set_cookie('h1', '你好', 120)
    # response.set_cookie('h1', '你好', None, datetime(2016, 10, 31))
    return response
  • delete_cookie(key):刪除指定的key的Cookie,若是key不存在則什麼也不發生

子類HttpResponseRedirect

  • 重定向,服務器端跳轉
  • 構造函數的第一個參數用來指定重定向的地址
在views1.py中
from django.http import HttpResponse,HttpResponseRedirect
def index(request):
    return HttpResponseRedirect('js/')
def index2(request,id):
    return HttpResponse(id)

在應用的urls.py中增長一個url對象
url(r'^([0-9]+)/$', views1.index2, name='index2'),
  • 請求地址欄如圖:

請求地址欄

  • 請求結果的地址欄如圖:

請求地址欄

  • 推薦使用反向解析
from django.core.urlresolvers import reverse

def index(request):
    return HttpResponseRedirect(reverse('booktest:index2', args=(1,)))

子類JsonResponse

  • 返回json數據,通常用於異步請求
  • _init _(data)
  • 幫助用戶建立JSON編碼的響應
  • 參數data是字典對象
  • JsonResponse的默認Content-Type爲application/json
from django.http import JsonResponse

def index2(requeset):
    return JsonResponse({'list': 'abc'})

簡寫函數

render

  • render(request, template_name[, context])
  • 結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的HttpResponse對象
  • request:該request用於生成response
  • template_name:要使用的模板的完整名稱
  • context:添加到模板上下文的一個字典,視圖將在渲染模板以前調用它
from django.shortcuts import render

def index(request):
    return render(request, 'booktest/index.html', {'h1': 'hello'})

重定向

  • redirect(to)
  • 爲傳遞進來的參數返回HttpResponseRedirect
  • to推薦使用反向解析
from django.shortcuts import redirect
from django.core.urlresolvers import reverse

def index(request):
    return redirect(reverse('booktest:index2'))

獲得對象或返回404

  • get_object_or_404(klass, args, *kwargs)
  • 經過模型管理器或查詢集調用get()方法,若是沒找到對象,不引起模型的DoesNotExist異常,而是引起Http404異常
  • klass:獲取對象的模型類、Manager對象或QuerySet對象
  • **kwargs:查詢的參數,格式應該能夠被get()和filter()接受
  • 若是找到多個對象將引起MultipleObjectsReturned異常
from django.shortcuts import *

def detail(request, id):
    try:
        book = get_object_or_404(BookInfo, pk=id)
    except BookInfo.MultipleObjectsReturned:
        book = None
    return render(request, 'booktest/detail.html', {'book': book})

將settings.py中的DEBUG改成False
將請求地址輸入2和100查看效果

獲得列表或返回404

  • get_list_or_404(klass, args, *kwargs)
  • klass:獲取列表的一個Model、Manager或QuerySet實例
  • **kwargs:查尋的參數,格式應該能夠被get()和filter()接受
from django.shortcuts import *

def index(request):
    # list = get_list_or_404(BookInfo, pk__lt=1)
    list = get_list_or_404(BookInfo, pk__lt=6)
    return render(request, 'booktest/index.html', {'list': list})

將settings.py中的DEBUG改成False

 

狀態保持

  • http協議是無狀態的:每次請求都是一次新的請求,不會記得以前通訊的狀態
  • 客戶端與服務器端的一次通訊,就是一次會話
  • 實現狀態保持的方式:在客戶端或服務器端存儲與會話有關的數據
  • 存儲方式包括cookie、session,會話通常指session對象
  • 使用cookie,全部數據存儲在客戶端,注意不要存儲敏感信息
  • 推薦使用sesison方式,全部數據存儲在服務器端,在客戶端cookie中存儲session_id
  • 狀態保持的目的是在一段時間內跟蹤請求者的狀態,能夠實現跨頁面訪問當前請求者的數據
  • 注意:不一樣的請求者之間不會共享這個數據,與請求者一一對應

啓用session

  • 使用django-admin startproject建立的項目默認啓用
  • 在settings.py文件中
項INSTALLED_APPS列表中添加:
'django.contrib.sessions',

項MIDDLEWARE_CLASSES列表中添加:
'django.contrib.sessions.middleware.SessionMiddleware',
  • 禁用會話:刪除上面指定的兩個值,禁用會話將節省一些性能消耗

使用session

  • 啓用會話後,每一個HttpRequest對象將具備一個session屬性,它是一個類字典對象
  • get(key, default=None):根據鍵獲取會話的值
  • clear():清除全部會話
  • flush():刪除當前的會話數據並刪除會話的Cookie
  • del request.session['member_id']:刪除會話

用戶登陸示例

  • 操做效果以下圖:

 

 

  • 在views.py文件中建立視圖
from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse

def index(request):
    uname = request.session.get('uname')
    return render(request, 'booktest/index.html', {'uname': uname})

def login(request):
    return render(request, 'booktest/login.html')

def login_handle(request):
    request.session['uname'] = request.POST['uname']
    return redirect(reverse('main:index'))

def logout(request):
    # request.session['uname'] = None
    # del request.session['uname']
    # request.session.clear()
    request.session.flush()
    return redirect(reverse('main:index'))
  • 配置url
主url:
from django.conf.urls import include, url
urlpatterns = [
    url(r'^', include('booktest.urls', namespace='main'))
]

應用url:
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'login/$', views.login, name='login'),
    url(r'login_handle/$', views.login_handle, name='login_handle'),
    url(r'logout/$', views.logout, name='logout')
]
  • 建立模板index.html
<!DOCTYPE html>
<html>
<head>
    <title>首頁</title>
</head>
<body>
你好:{{uname}}
<hr/>
<a href="{%url 'main:login'%}">登陸</a>
<hr/>
<a href="{%url 'main:logout'%}">退出</a>
</body>
</html>
  • 建立模板login.html
<!DOCTYPE html>
<html>
<head>
    <title>登陸</title>
</head>
<body>
<form method="post" action="/login_handle/">
    <input type="text" name="uname"/>
    <input type="submit" value="登陸"/>
</form>
</body>
</html>

會話過時時間

  • set_expiry(value):設置會話的超時時間
  • 若是沒有指定,則兩個星期後過時
  • 若是value是一個整數,會話將在values秒沒有活動後過時
  • 若果value是一個imedelta對象,會話將在當前時間加上這個指定的日期/時間過時
  • 若是value爲0,那麼用戶會話的Cookie將在用戶的瀏覽器關閉時過時
  • 若是value爲None,那麼會話永不過時
  • 修改視圖中login_handle函數,查看效果
def login_handle(request):
    request.session['uname'] = request.POST['uname']
    # request.session.set_expiry(10)
    # request.session.set_expiry(timedelta(days=5))
    # request.session.set_expiry(0)
    # request.session.set_expiry(None)
    return redirect(reverse('main:index'))

存儲session

  • 使用存儲會話的方式,可使用settings.py的SESSION_ENGINE項指定
  • 基於數據庫的會話:這是django默認的會話存儲方式,須要添加django.contrib.sessions到的INSTALLED_APPS設置中,運行manage.py migrate在數據庫中安裝會話表,可顯示指定爲
SESSION_ENGINE='django.contrib.sessions.backends.db'
  • 基於緩存的會話:只存在本地內在中,若是丟失則不能找回,比數據庫的方式讀寫更快
SESSION_ENGINE='django.contrib.sessions.backends.cache'
  • 能夠將緩存和數據庫同時使用:優先從本地緩存中獲取,若是沒有則從數據庫中獲取
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'

使用Redis緩存session

  • 會話還支持文件、純cookie、Memcached、Redis等方式存儲,下面演示使用redis存儲
  • 安裝包
pip install django-redis-sessions
  • 修改settings中的配置,增長以下項
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'
  • 管理redis的命令
啓動:sudo redis-server /etc/redis/redis.conf
中止:sudo redis-server stop
重啓:sudo redis-server restart
redis-cli:使用客戶端鏈接服務器
keys *:查看全部的鍵
get name:獲取指定鍵的值
del name:刪除指定名稱的鍵

總結

  • 定義視圖
  • 配置url
  • request對象
  • response對象
  • 狀態保持

做業

  • 熟練完成當天代碼
  • 完成用戶登陸、註冊

模板介紹

  • 做爲Web框架,Django提供了模板,能夠很便利的動態生成HTML
  • 模版系統致力於表達外觀,而不是程序邏輯
  • 模板的設計實現了業務邏輯(view)與顯示內容(template)的分離,一個視圖可使用任意一個模板,一個模板能夠供多個視圖使用
  • 模板包含
    • HTML的靜態部分
    • 動態插入內容部分
  • Django模板語言,簡寫DTL,定義在django.template包中
  • 由startproject命令生成的settings.py定義關於模板的值:
    • DIRS定義了一個目錄列表,模板引擎按列表順序搜索這些目錄以查找模板源文件
    • APP_DIRS告訴模板引擎是否應該在每一個已安裝的應用中查找模板
  • 經常使用方式:在項目的根目錄下建立templates目錄,設置DIRS值
DIRS=[os.path.join(BASE_DIR,"templates")]

模板處理

  • Django處理模板分爲兩個階段
  • Step1 加載:根據給定的標識找到模板而後預處理,一般會將它編譯好放在內存中
loader.get_template(template_name),返回一個Template對象
  • Step2 渲染:使用Context數據對模板插值並返回生成的字符串
Template對象的render(RequestContext)方法,使用context渲染模板
  • 加載渲染完整代碼:
from django.template import loader, RequestContext
from django.http import HttpResponse

def index(request):
    tem = loader.get_template('temtest/index.html')
    context = RequestContext(request, {})
    return HttpResponse(tem.render(context))

快捷函數

  • 爲了減小加載模板、渲染模板的重複代碼,django提供了快捷函數
  • render_to_string("")
  • render(request,'模板',context)
from django.shortcuts import render

def index(request):
    return render(request, 'temtest/index.html')

 

定義模板

  • 模板語言包括
    • 變量
    • 標籤 { % 代碼塊 % }
    • 過濾器
    • 註釋{# 代碼或html #}

變量

  • 語法:
{{ variable }}
  • 當模版引擎遇到一個變量,將計算這個變量,而後將結果輸出
  • 變量名必須由字母、數字、下劃線(不能如下劃線開頭)和點組成
  • 當模版引擎遇到點("."),會按照下列順序查詢:
    1. 字典查詢,例如:foo["bar"]
    2. 屬性或方法查詢,例如:foo.bar
    3. 數字索引查詢,例如:foo[bar]
  • 若是變量不存在, 模版系統將插入'' (空字符串)
  • 在模板中調用方法時不能傳遞參數

在模板中調用對象的方法

  • 在models.py中定義類HeroInfo
from django.db import models

class HeroInfo(models.Model):
    ...
    def showName(self):
        return self.hname
  • 在views.py中傳遞HeroInfo對象
from django.shortcuts import render
from models import *

def index(request):
    hero = HeroInfo(hname='abc')
    context = {'hero': hero}
    return render(request, 'temtest/detail.html', context)
  • 在模板detail.html中調用
{{hero.showName}}

標籤

  • 語法:{ % tag % }
  • 做用
    • 在輸出中建立文本
    • 控制循環或邏輯
    • 加載外部信息到模板中供之後的變量使用
  • for標籤
{ %for ... in ...%}
循環邏輯
{{forloop.counter}}表示當前是第幾回循環
{ %empty%}
給出的列表爲或列表不存在時,執行此處
{ %endfor%}
  • if標籤
{ %if ...%}
邏輯1
{ %elif ...%}
邏輯2
{ %else%}
邏輯3
{ %endif%}
  • comment標籤
{ % comment % }
多行註釋
{ % endcomment % }
  • include:加載模板並以標籤內的參數渲染
{ %include "foo/bar.html" % }
  • url:反向解析
{ % url 'name' p1 p2 %}
  • csrf_token:這個標籤用於跨站請求僞造保護
{ % csrf_token %}
  • 布爾標籤:and、or,and比or的優先級高
  • block、extends:詳見「模板繼承」
  • autoescape:詳見「HTML轉義」

過濾器

  • 語法:{ { 變量|過濾器 }},例如{ { name|lower }},表示將變量name的值變爲小寫輸出
  • 使用管道符號 (|)來應用過濾器
  • 經過使用過濾器來改變變量的計算結果
  • 能夠在if標籤中使用過濾器結合運算符
if list1|length > 1
  • 過濾器可以被「串聯」,構成過濾器鏈
name|lower|upper
  • 過濾器能夠傳遞參數,參數使用引號包起來
list|join:", "
  • default:若是一個變量沒有被提供,或者值爲false或空,則使用默認值,不然使用變量的值
value|default:"什麼也沒有"
  • date:根據給定格式對一個date變量格式化
value|date:'Y-m-d'
  • escape:詳見「HTML轉義」
  • 點擊查看詳細的過濾器

註釋

  • 單行註釋
{#...#}
  • 註釋能夠包含任何模版代碼,有效的或者無效的均可以
{# { % if foo % }bar{ % else % } #}
  • 使用comment標籤註釋模版中的多行內容

示例

  • 查詢全部英雄信息顯示出來,要求奇數行顯示爲紅色,偶數行顯示爲藍色

模板繼承

  • 模板繼承能夠減小頁面內容的重複定義,實現頁面內容的重用
  • 典型應用:網站的頭部、尾部是同樣的,這些內容能夠定義在父模板中,子模板不須要重複定義
  • block標籤:在父模板中預留區域,在子模板中填充
  • extends繼承:繼承,寫在模板文件的第一行
  • 定義父模板base.html
{ %block block_name%}
這裏能夠定義默認值
若是不定義默認值,則表示空字符串
{ %endblock%}
  • 定義子模板index.html
{ % extends "base.html" %}
  • 在子模板中使用block填充預留區域
{ %block block_name%}
實際填充內容
{ %endblock%}

說明

  • 若是在模版中使用extends標籤,它必須是模版中的第一個標籤
  • 不能在一個模版中定義多個相同名字的block標籤
  • 子模版沒必要定義所有父模版中的blocks,若是子模版沒有定義block,則使用了父模版中的默認值
  • 若是發如今模板中大量的複製內容,那就應該把內容移動到父模板中
  • 使用能夠獲取父模板中block的內容
  • 爲了更好的可讀性,能夠給endblock標籤一個名字
{ % block block_name %}
區域內容
{ % endblock block_name %}

三層繼承結構

  • 三層繼承結構使代碼獲得最大程度的複用,而且使得添加內容更加簡單
  • 以下圖爲常見的電商頁面

 

1.建立根級模板

  • 名稱爲「base.html」
  • 存放整個站點共用的內容
<!DOCTYPE html>
<html>
<head>
    <title>{%block title%}{%endblock%} 水果超市</title>
</head>
<body>
top--{{logo}}
<hr/>
{%block left%}{%endblock%}
{%block content%}{%endblock%}
<hr/>
bottom
</body>
</html>

2.建立分支模版

  • 繼承自base.html
  • 名爲「base_*.html」
  • 定義特定分支共用的內容
  • 定義base_goods.html
{%extends 'temtest/base.html'%}
{%block title%}商品{%endblock%}
{%block left%}
<h1>goods left</h1>
{%endblock%}
  • 定義base_user.html
{%extends 'temtest/base.html'%}
{%block title%}用戶中心{%endblock%}
{%block left%}
<font color='blue'>user left</font>
{%endblock%}
  • 定義index.html,繼承自base.html,不須要寫left塊
{%extends 'temtest/base.html'%}
{%block content%}
首頁內容
{%endblock content%}

3.爲具體頁面建立模板,繼承自分支模板

  • 定義商品列表頁goodslist.html
{%extends 'temtest/base_goods.html'%}
{%block content%}
商品正文列表
{%endblock content%}
  • 定義用戶密碼頁userpwd.html
{%extends 'temtest/base_user.html'%}
{%block content%}
用戶密碼修改
{%endblock content%}

4.視圖調用具體頁面,並傳遞模板中須要的數據

  • 首頁視圖index
logo='welcome to itcast'
def index(request):
    return render(request, 'temtest/index.html', {'logo': logo})
  • 商品列表視圖goodslist
def goodslist(request):
    return render(request, 'temtest/goodslist.html', {'logo': logo})
  • 用戶密碼視圖userpwd
def userpwd(request):
    return render(request, 'temtest/userpwd.html', {'logo': logo})

5.配置url

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^list/$', views.goodslist, name='list'),
    url(r'^pwd/$', views.userpwd, name='pwd'),
]

 

HTML轉義

  • Django對字符串進行自動HTML轉義,如在模板中輸出以下值:
視圖代碼:
def index(request):
    return render(request, 'temtest/index2.html',
                  {
                      't1': '<h1>hello</h1>'
                  })
模板代碼:
{{t1}}
  • 顯示效果以下圖:

會被自動轉義的字符

  • html轉義,就是將包含的html標籤輸出,而不被解釋執行,緣由是當顯示用戶提交字符串時,可能包含一些攻擊性的代碼,如js腳本
  • Django會將以下字符自動轉義:
< 會轉換爲&lt;

> 會轉換爲&gt;

' (單引號) 會轉換爲&#39;

" (雙引號)會轉換爲 &quot;

& 會轉換爲 &amp;
  • 當顯示不被信任的變量時使用escape過濾器,通常省略,由於Django自動轉義
{{t1|escape}}

關閉轉義

  • 對於變量使用safe過濾器
{{ data|safe }}
  • 對於代碼塊使用autoescape標籤
{ % autoescape off %}
{{ body }}
{ % endautoescape %}
  • 標籤autoescape接受on或者off參數
  • 自動轉義標籤在base模板中關閉,在child模板中也是關閉的

字符串字面值

  • 手動轉義
{ { data|default:"<b>123</b>" }}
  • 應寫爲
{ { data|default:"&lt;b&gt;123&lt;/b&gt;" }}

 

csrf

  • 全稱Cross Site Request Forgery,跨站請求僞造
  • 某些惡意網站上包含連接、表單按鈕或者JavaScript,它們會利用登陸過的用戶在瀏覽器中的認證信息試圖在你的網站上完成某些操做,這就是跨站攻擊
  • 演示csrf以下
  • 建立視圖csrf1用於展現表單,csrf2用於接收post請求
def csrf1(request):
    return render(request,'booktest/csrf1.html')
def csrf2(request):
    uname=request.POST['uname']
    return render(request,'booktest/csrf2.html',{'uname':uname})
  • 配置url
url(r'^csrf1/$', views.csrf1),
url(r'^csrf2/$', views.csrf2),
  • 建立模板csrf1.html用於展現表單
<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="/crsf2/">
    <input name="uname"><br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • 建立模板csrf2用於展現接收的結果
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}
</body>
</html>
  • 在瀏覽器中訪問,查看效果,報錯以下:

 

  • 將settings.py中的中間件代碼'django.middleware.csrf.CsrfViewMiddleware'註釋
  • 查看csrf1的源代碼,複製,在本身的網站內建一個html文件,粘貼源碼,訪問查看效果

防csrf的使用

  • 在django的模板中,提供了防止跨站攻擊的方法,使用步驟以下:
  • step1:在settings.py中啓用'django.middleware.csrf.CsrfViewMiddleware'中間件,此項在建立項目時,默認被啓用
  • step2:在csrf1.html中添加標籤
<form>
{% csrf_token %}
...
</form>
  • step3:測試剛纔的兩個請求,發現跨站的請求被拒絕了,效果以下圖

 

取消保護

  • 若是某些視圖不須要保護,可使用裝飾器csrf_exempt,模板中也不須要寫標籤,修改csrf2的視圖以下
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def csrf2(request):
    uname=request.POST['uname']
    return render(request,'booktest/csrf2.html',{'uname':uname})
  • 運行上面的兩個請求,發現均可以請求

保護原理

  • 加入標籤後,能夠查看源代碼,發現多了以下代碼
<input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
  • 在瀏覽器的調試工具中,經過network標籤能夠查看cookie信息
  • 本站中自動添加了cookie信息,以下圖

 

  • 查看跨站的信息,並無cookie信息,即便加入上面的隱藏域代碼,發現又能夠訪問了
  • 結論:django的csrf不是徹底的安全
  • 當提交請求時,中間件'django.middleware.csrf.CsrfViewMiddleware'會對提交的cookie及隱藏域的內容進行驗證,若是失敗則返回403錯誤

 

驗證碼

  • 在用戶註冊、登陸頁面,爲了防止暴力請求,能夠加入驗證碼功能,若是驗證碼錯誤,則不須要繼續處理,能夠減輕一些服務器的壓力
  • 使用驗證碼也是一種有效的防止crsf的方法
  • 驗證碼效果以下圖:

 

驗證碼視圖

  • 新建viewsUtil.py,定義函數verifycode
  • 此段代碼用到了PIL中的Image、ImageDraw、ImageFont模塊,須要先安裝Pillow(3.4.1)包,詳細文檔參考http://pillow.readthedocs.io/en/3.4.x/
  • Image表示畫布對象
  • ImageDraw表示畫筆對象
  • ImageFont表示字體對象,ubuntu的字體路徑爲「/usr/share/fonts/truetype/freefont」

  • 代碼以下:

from django.http import HttpResponse
def verifycode(request):
    #引入繪圖模塊
    from PIL import Image, ImageDraw, ImageFont
    #引入隨機函數模塊
    import random
    #定義變量,用於畫面的背景色、寬、高
    bgcolor = (random.randrange(20, 100), random.randrange(
        20, 100), 255)
    width = 100
    height = 25
    #建立畫面對象
    im = Image.new('RGB', (width, height), bgcolor)
    #建立畫筆對象
    draw = ImageDraw.Draw(im)
    #調用畫筆的point()函數繪製噪點
    for i in range(0, 100):
        xy = (random.randrange(0, width), random.randrange(0, height))
        fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
        draw.point(xy, fill=fill)
    #定義驗證碼的備選值
    str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
    #隨機選取4個值做爲驗證碼
    rand_str = ''
    for i in range(0, 4):
        rand_str += str1[random.randrange(0, len(str1))]
    #構造字體對象
    font = ImageFont.truetype('FreeMono.ttf', 23)
    #構造字體顏色
    fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    #繪製4個字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
    #釋放畫筆
    del draw
    #存入session,用於作進一步驗證
    request.session['verifycode'] = rand_str
    #內存文件操做
    import cStringIO
    buf = cStringIO.StringIO()
    #將圖片保存在內存中,文件類型爲png
    im.save(buf, 'png')
    #將內存中的圖片數據返回給客戶端,MIME類型爲圖片png
    return HttpResponse(buf.getvalue(), 'image/png')

配置url

  • 在urls.py中定義請求驗證碼視圖的url
from . import viewsUtil

urlpatterns = [
    url(r'^verifycode/$', viewsUtil.verifycode),
]

顯示驗證碼

  • 在模板中使用img標籤,src指向驗證碼視圖
<img id='verifycode' src="/verifycode/" alt="CheckCode"/>
  • 啓動服務器,查看顯示成功
  • 擴展:點擊「看不清,換一個」時,能夠換一個新的驗證碼
<script type="text/javascript" src="/static/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
    $(function(){
        $('#verifycodeChange').css('cursor','pointer').click(function() {
            $('#verifycode').attr('src',$('#verifycode').attr('src')+1)
        });
    });
</script>
<img id='verifycode' src="/verifycode/?1" alt="CheckCode"/>
<span id='verifycodeChange'>看不清,換一個</span>
  • 爲了可以實現提交功能,須要增長form和input標籤
<form method='post' action='/verifycodeValid/'>
    <input type="text" name="vc">
    <img id='verifycode' src="/verifycode/?1" alt="CheckCode"/>
<span id='verifycodeChange'>看不清,換一個</span>
<br>
<input type="submit" value="提交">
</form>

驗證

  • 接收請求的信息,與session中的內容對比
from django.http import HttpResponse

def verifycodeValid(request):
    vc = request.POST['vc']
    if vc.upper() == request.session['verifycode']:
        return HttpResponse('ok')
    else:
        return HttpResponse('no')
  • 配置驗證處理的url
urlpatterns = [
    url(r'^verifycodeValid/$', views.verifycodeValid),
]

第三方

  • 能夠在網上搜索「驗證碼」,找到一些第三方驗證碼提供網站,閱讀文檔,使用到項目中

總結

  • 模板語法
  • 模板繼承
  • html轉義
  • csrf

做業

  • 熟練完成當天代碼
  • 查找使用一個第三方的驗證碼

 

高級知識點包括:

  • 靜態文件處理
  • 中間件
  • 上傳圖片
  • Admin站點
  • 分頁
  • 使用jquery完成ajax

管理靜態文件

  • 項目中的CSS、圖片、js都是靜態文件

配置靜態文件

  • 在settings 文件中定義靜態內容
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
  • 在項目根目錄下建立static目錄,再建立當前應用名稱的目錄
mysite/static/myapp/
  • 在模板中可使用硬編碼
/static/my_app/myexample.jpg
  • 在模板中可使用static編碼
{ % load static from staticfiles %}
<img src="{ % static "my_app/myexample.jpg" %}" alt="My image"/>

 

中間件

  • 是一個輕量級、底層的插件系統,能夠介入Django的請求和響應處理過程,修改Django的輸入或輸出
  • 激活:添加到Django配置文件中的MIDDLEWARE_CLASSES元組中
  • 每一箇中間件組件是一個獨立的Python類,能夠定義下面方法中的一個或多個
    • _init _:無需任何參數,服務器響應第一個請求的時候調用一次,用於肯定是否啓用當前中間件
    • process_request(request):執行視圖以前被調用,在每一個請求上調用,返回None或HttpResponse對象
    • process_view(request, view_func, view_args, view_kwargs):調用視圖以前被調用,在每一個請求上調用,返回None或HttpResponse對象
    • process_template_response(request, response):在視圖恰好執行完畢以後被調用,在每一個請求上調用,返回實現了render方法的響應對象
    • process_response(request, response):全部響應返回瀏覽器以前被調用,在每一個請求上調用,返回HttpResponse對象
    • process_exception(request,response,exception):當視圖拋出異常時調用,在每一個請求上調用,返回一個HttpResponse對象
  • 使用中間件,能夠干擾整個處理過程,每次請求中都會執行中間件的這個方法
  • 示例:自定義異常處理
  • 與settings.py同級目錄下建立myexception.py文件,定義類MyException,實現process_exception方法
from django.http import HttpResponse
class MyException():
    def process_exception(request,response, exception):
        return HttpResponse(exception.message)
  • 將類MyException註冊到settings.py中間件中
MIDDLEWARE_CLASSES = (
    'test1.myexception.MyException',
    ...
)
  • 定義視圖,併發生一個異常信息,則會運行自定義的異常處理

上傳圖片

  • 當Django在處理文件上傳的時候,文件數據被保存在request.FILES
  • FILES中的每一個鍵爲<input type="file" name="" />中的name
  • 注意:FILES只有在請求的方法爲POST 且提交的<form>帶有enctype="multipart/form-data" 的狀況下才會包含數據。不然,FILES 將爲一個空的相似於字典的對象
  • 使用模型處理上傳文件:將屬性定義成models.ImageField類型
pic=models.ImageField(upload_to='cars/')
  • 注意:若是屬性類型爲ImageField須要安裝包Pilow
pip install Pillow==3.4.1
  • 圖片存儲路徑
    • 在項目根目錄下建立media文件夾
    • 圖片上傳後,會被保存到「/static/media/cars/圖片文件」
    • 打開settings.py文件,增長media_root項
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
  • 使用django後臺管理,遇到ImageField類型的屬性會出現一個file框,完成文件上傳
  • 手動上傳的模板代碼
<html>
<head>
    <title>文件上傳</title>
</head>
<body>
    <form method="post" action="upload/" enctype="multipart/form-data">
        <input type="text" name="title"><br>
        <input type="file" name="pic"/><br>
        <input type="submit" value="上傳">
    </form>
</body>
</html>
  • 手動上傳的視圖代碼
from django.conf import settings

def upload(request):
    if request.method == "POST":
        f1 = request.FILES['pic']
        fname = '%s/cars/%s' % (settings.MEDIA_ROOT,f1.name)
        with open(fname, 'w') as pic:
            for c in f1.chunks():
                pic.write(c)
        return HttpResponse("ok")
    else:
        return HttpResponse("error")

 

Admin站點

  • 經過使用startproject建立的項目模版中,默認Admin被啓用
  • 1.建立管理員的用戶名和密碼
python manage.py createsuperuser
而後按提示填寫用戶名、郵箱、密碼
  • 2.在應用內admin.py文件完成註冊,就能夠在後臺管理中維護模型的數據
from django.contrib import admin
from models import *

admin.site.register(HeroInfo)
  • 查找admin文件:在INSTALLED_APPS項中加入django.contrib.admin,Django就會自動搜索每一個應用的admin模塊並將其導入

ModelAdmin對象

  • ModelAdmin類是模型在Admin界面中的表示形式
  • 定義:定義一個類,繼承於admin.ModelAdmin,註冊模型時使用這個類
class HeroAdmin(admin.ModelAdmin):
    ...
  • 一般定義在應用的admin.py文件裏
  • 使用方式一:註冊參數
admin.site.register(HeroInfo,HeroAdmin)
  • 使用方式二:註冊裝飾器
@admin.register(HeroInfo)
class HeroAdmin(admin.ModelAdmin):
  • 經過重寫admin.ModelAdmin的屬性規定顯示效果,屬性主要分爲列表頁、增長修改頁兩部分

列表頁選項

「操做選項」的位置

  • actions_on_top、actions_on_bottom:默認顯示在頁面的頂部
class HeroAdmin(admin.ModelAdmin):
    actions_on_top = True
    actions_on_bottom = True

list_display

  • 出現列表中顯示的字段
  • 列表類型
  • 在列表中,能夠是字段名稱,也能夠是方法名稱,可是方法名稱默認不能排序
  • 在方法中可使用format_html()輸出html內容
在models.py文件中
from django.db import models
from tinymce.models import HTMLField
from django.utils.html import format_html

class HeroInfo(models.Model):
    hname = models.CharField(max_length=10)
    hcontent = HTMLField()
    isDelete = models.BooleanField()
    def hContent(self):
        return format_html(self.hcontent)

在admin.py文件中
class HeroAdmin(admin.ModelAdmin):
    list_display = ['hname', 'hContent']
  • 讓方法排序,爲方法指定admin_order_field屬性
在models.py中HeroInfo類的代碼改成以下:
    def hContent(self):
        return format_html(self.hcontent)
    hContent.admin_order_field = 'hname'
  • 標題欄名稱:將字段封裝成方法,爲方法設置short_description屬性
在models.py中爲HeroInfo類增長方法hName:
    def hName(self):
        return self.hname
    hName.short_description = '姓名'
    hContent.short_description = '內容'

在admin.py頁中註冊
class HeroAdmin(admin.ModelAdmin):
    list_display = ['hName', 'hContent']

list_filter

  • 右側欄過濾器,對哪些屬性的值進行過濾
  • 列表類型
  • 只能接收字段
class HeroAdmin(admin.ModelAdmin):
    ...
    list_filter = ['hname', 'hcontent']

list_per_page

  • 每頁中顯示多少項,默認設置爲100
class HeroAdmin(admin.ModelAdmin):
    ...
    list_per_page = 10

search_fields

  • 搜索框
  • 列表類型,表示在這些字段上進行搜索
  • 只能接收字段
class HeroAdmin(admin.ModelAdmin):
    ...
    search_fields = ['hname']

增長與修改頁選項

  • fields:顯示字段的順序,若是使用元組表示顯示到一行上
class HeroAdmin(admin.ModelAdmin):
    ...
    fields = [('hname', 'hcontent')]
  • fieldsets:分組顯示
class HeroAdmin(admin.ModelAdmin):
    ...
    fieldsets = (
        ('base', {'fields': ('hname')}),
        ('other', {'fields': ('hcontent')})
    )
  • fields與fieldsets二者選一

InlineModelAdmin對象

  • 類型InlineModelAdmin:表示在模型的添加或修改頁面嵌入關聯模型的添加或修改
  • 子類TabularInline:以表格的形式嵌入
  • 子類StackedInline:以塊的形式嵌入
class HeroInline(admin.TabularInline):
    model = HeroInfo

class BookAdmin(admin.ModelAdmin):
    inlines = [
        HeroInline,
    ]

重寫admin模板

  • 在項目所在目錄中建立templates目錄,再建立一個admin目錄
  • 設置模板查找目錄:修改settings.py的TEMPLATES項,加載模板時會在DIRS列表指定的目錄中搜索
'DIRS': [os.path.join(BASE_DIR, 'templates')],
  • 從Django安裝的目錄下(django/contrib/admin/templates)將模板頁面的源文件admin/base_site.html拷貝到第一步建好的目錄裏
  • 編輯base_site.html文件
  • 刷新頁面,發現以剛纔編輯的頁面效果顯示
  • 其它管理後臺的模板能夠按照相同的方式進行修改

分頁

  • Django提供了一些類實現管理數據分頁,這些類位於django/core/paginator.py中

Paginator對象

  • Paginator(列表,int):返回分頁對象,參數爲列表數據,每面數據的條數

屬性

  • count:對象總數
  • num_pages:頁面總數
  • page_range:頁碼列表,從1開始,例如[1, 2, 3, 4]

方法

  • page(num):下標以1開始,若是提供的頁碼不存在,拋出InvalidPage異常

異常exception

  • InvalidPage:當向page()傳入一個無效的頁碼時拋出
  • PageNotAnInteger:當向page()傳入一個不是整數的值時拋出
  • EmptyPage:當向page()提供一個有效值,可是那個頁面上沒有任何對象時拋出

Page對象

建立對象

  • Paginator對象的page()方法返回Page對象,不須要手動構造

屬性

  • object_list:當前頁上全部對象的列表
  • number:當前頁的序號,從1開始
  • paginator:當前page對象相關的Paginator對象

方法

  • has_next():若是有下一頁返回True
  • has_previous():若是有上一頁返回True
  • has_other_pages():若是有上一頁或下一頁返回True
  • next_page_number():返回下一頁的頁碼,若是下一頁不存在,拋出InvalidPage異常
  • previous_page_number():返回上一頁的頁碼,若是上一頁不存在,拋出InvalidPage異常
  • len():返回當前頁面對象的個數
  • 迭代頁面對象:訪問當前頁面中的每一個對象

示例

建立視圖pagTest

from django.core.paginator import Paginator

def pagTest(request, pIndex):
    list1 = AreaInfo.objects.filter(aParent__isnull=True)
    p = Paginator(list1, 10)
    if pIndex == '':
        pIndex = '1'
    pIndex = int(pIndex)
    list2 = p.page(pIndex)
    plist = p.page_range
    return render(request, 'booktest/pagTest.html', {'list': list2, 'plist': plist, 'pIndex': pIndex})

配置url

url(r'^pag(?P<pIndex>[0-9]*)/$', views.pagTest, name='pagTest'),

定義模板pagTest.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<ul>
{%for area in list%}
<li>{{area.id}}--{{area.atitle}}</li>
{%endfor%}
</ul>

{%for pindex in plist%}
{%if pIndex == pindex%}
{{pindex}}&nbsp;&nbsp;
{%else%}
<a href="/pag{{pindex}}/">{{pindex}}</a>&nbsp;&nbsp;
{%endif%}
{%endfor%}
</body>
</html>

 

使用Ajax

  • 使用視圖經過上下文向模板中傳遞數據,須要先加載完成模板的靜態頁面,再執行模型代碼,生成最張的html,返回給瀏覽器,這個過程將頁面與數據集成到了一塊兒,擴展性差
  • 改進方案:經過ajax的方式獲取數據,經過dom操做將數據呈現到界面上
  • 推薦使用框架的ajax相關方法,不要使用XMLHttpRequest對象,由於操做麻煩且不容易查錯
  • jquery框架中提供了$.ajax、$.get、$.post方法,用於進行異步交互
  • 因爲csrf的約束,推薦使用$.get
  • 示例:實現省市區的選擇
  • 最終實現效果如圖:

 

引入js文件

  • js文件屬於靜態文件,建立目錄結構如圖:

 

 

修改settings.py關於靜態文件的設置

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

在models.py中定義模型

class AreaInfo(models.Model):
    aid = models.IntegerField(primary_key=True)
    atitle = models.CharField(max_length=20)
    aPArea = models.ForeignKey('AreaInfo', null=True)

生成遷移

python manage.py makemigrations
python manage.py migrate

經過workbench向表中填充示例數據

  • 參見「省市區.sql」
  • 注意將表的名稱完成替換

在views.py中編寫視圖

  • index用於展現頁面
  • getArea1用於返回省級數據
  • getArea2用於根據省、市編號返回市、區信息,格式都爲字典對象
from django.shortcuts import render
from django.http import JsonResponse
from models import AreaInfo

def index(request):
    return render(request, 'ct1/index.html')

def getArea1(request):
    list = AreaInfo.objects.filter(aPArea__isnull=True)
    list2 = []
    for a in list:
        list2.append([a.aid, a.atitle])
    return JsonResponse({'data': list2})

def getArea2(request, pid):
    list = AreaInfo.objects.filter(aPArea_id=pid)
    list2 = []
    for a in list:
        list2.append({'id': a.aid, 'title': a.atitle})
    return JsonResponse({'data': list2})

在urls.py中配置urlconf

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index),
    url(r'^area1/$', views.getArea1),
    url(r'^([0-9]+)/$', views.getArea2),
]

主urls.py中包含此應用的url

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^', include('ct1.urls', namespace='ct1')),
    url(r'^admin/', include(admin.site.urls)),
]

定義模板index.html

  • 在項目中的目錄結構如圖:

 

  • 修改settings.py的TEMPLATES項,設置DIRS值
'DIRS': [os.path.join(BASE_DIR, 'templates')],
  • 定義模板文件:包含三個select標籤,分別存放省市區的信息
<!DOCTYPE html>
<html>
<head>
    <title>省市區列表</title>
</head>
<body>
<select id="pro">
    <option value="">請選擇省</option>
</select>
<select id="city">
    <option value="">請選擇市</option>
</select>
<select id="dis">
    <option value="">請選擇區縣</option>
</select>
</body>
</html>

在模板中引入jquery文件

<script type="text/javascript" src="static/ct1/js/jquery-1.12.4.min.js"></script>

編寫js代碼

  • 綁定change事件
  • 發出異步請求
  • 使用dom添加元素
<script type="text/javascript">
        $(function(){

            $.get('area1/',function(dic) {
                pro=$('#pro')
                $.each(dic.data,function(index,item){
                    pro.append('<option value='+item[0]+'>'+item[1]+'</option>');
                })
            });

            $('#pro').change(function(){
                $.post($(this).val()+'/',function(dic){
                    city=$('#city');
                    city.empty().append('<option value="">請選擇市</option>');
                    $.each(dic.data,function(index,item){
                        city.append('<option value='+item.id+'>'+item.title+'</option>');
                    })
                });
            });

            $('#city').change(function(){
                $.post($(this).val()+'/',function(dic){
                    dis=$('#dis');
                    dis.empty().append('<option value="">請選擇區縣</option>');
                    $.each(dic.data,function(index,item){
                        dis.append('<option value='+item.id+'>'+item.title+'</option>');
                    })
                })
            });

        });
    </script>

 

總結

  • 靜態文件處理
  • 中間件
  • 上傳圖片
  • Admin站點
  • 分頁
  • 使用jquery完成ajax

做業

  • 熟練完成當天代碼

經常使用的第三方包或工具

  • 富文本編輯器
  • 緩存
  • 全文檢索
  • celery
  • 佈署

 

富文本編輯器

  • 藉助富文本編輯器,管理員可以編輯出來一個包含html的頁面,從而頁面的顯示效果,能夠由管理員定義,而不用徹底依賴於前期開發人員
  • 此處以tinymce爲例,其它富文本編輯器的使用能夠自行學習
  • 使用編輯器的顯示效果爲:

 

下載安裝

  • 在網站pypi網站搜索並下載"django-tinymce-2.4.0"
  • 解壓
tar zxvf django-tinymce-2.4.0.tar.gz
  • 進入解壓後的目錄,工做在虛擬環境,安裝
python setup.py install

應用到項目中

  • 在settings.py中爲INSTALLED_APPS添加編輯器應用
INSTALLED_APPS = (
    ...
    'tinymce',
)
  • 在settings.py中添加編輯配置項
TINYMCE_DEFAULT_CONFIG = {
    'theme': 'advanced',
    'width': 600,
    'height': 400,
}
  • 在根urls.py中配置
urlpatterns = [
    ...
    url(r'^tinymce/', include('tinymce.urls')),
]
  • 在應用中定義模型的屬性
from django.db import models
from tinymce.models import HTMLField

class HeroInfo(models.Model):
    ...
    hcontent = HTMLField()
  • 在後臺管理界面中,就會顯示爲富文本編輯器,而不是多行文本框

自定義使用

  • 定義視圖editor,用於顯示編輯器並完成提交
def editor(request):
    return render(request, 'other/editor.html')
  • 配置url
urlpatterns = [
    ...
    url(r'^editor/$', views.editor, name='editor'),
]
  • 建立模板editor.html
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script type="text/javascript" src='/static/tiny_mce/tiny_mce.js'></script>
    <script type="text/javascript">
        tinyMCE.init({
            'mode':'textareas',
            'theme':'advanced',
            'width':400,
            'height':100
        });
    </script>
</head>
<body>
<form method="post" action="/content/">
    <input type="text" name="hname">
    <br>
    <textarea name='hcontent'>哈哈,這是啥呀</textarea>
    <br>
    <input type="submit" value="提交">
</form>
</body>
</html>
  • 定義視圖content,接收請求,並更新heroinfo對象
def content(request):
    hname = request.POST['hname']
    hcontent = request.POST['hcontent']

    heroinfo = HeroInfo.objects.get(pk=1)
    heroinfo.hname = hname
    heroinfo.hcontent = hcontent
    heroinfo.save()

    return render(request, 'other/content.html', {'hero': heroinfo})
  • 添加url項
urlpatterns = [
    ...
    url(r'^content/$', views.content, name='content'),
]
  • 定義模板content.html
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
姓名:{{hero.hname}}
<hr>
{%autoescape off%}
{{hero.hcontent}}
{%endautoescape%}
</body>
</html>

 

緩存

  • 對於中等流量的網站來講,儘量地減小開銷是必要的。緩存數據就是爲了保存那些須要不少計算資源的結果,這樣的話就沒必要在下次重複消耗計算資源
  • Django自帶了一個健壯的緩存系統來保存動態頁面,避免對於每次請求都從新計算
  • Django提供了不一樣級別的緩存粒度:能夠緩存特定視圖的輸出、能夠僅僅緩存那些很難生產出來的部分、或者能夠緩存整個網站

設置緩存

  • 經過設置決定把數據緩存在哪裏,是數據庫中、文件系統仍是在內存中
  • 經過setting文件的CACHES配置來實現
  • 參數TIMEOUT:緩存的默認過時時間,以秒爲單位,這個參數默認是300秒,即5分鐘;設置TIMEOUT爲None表示永遠不會過時,值設置成0形成緩存當即失效
CACHES={
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'TIMEOUT': 60,
    }
}
  • 能夠將cache存到redis中,默認採用1數據庫,須要安裝包並配置以下:
安裝包:pip install django-redis-cache

CACHES = {
    "default": {
        "BACKEND": "redis_cache.cache.RedisCache",
        "LOCATION": "localhost:6379",
        'TIMEOUT': 60,
    },
}
  • 能夠鏈接redis查看存的數據
鏈接:redis-cli
切換數據庫:select 1
查看鍵:keys *
查看值:get 鍵

單個view緩存

  • django.views.decorators.cache定義了cache_page裝飾器,用於對視圖的輸出進行緩存
  • 示例代碼以下:
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def index(request):
    return HttpResponse('hello1')
    #return HttpResponse('hello2')
  • cache_page接受一個參數:timeout,秒爲單位,上例中緩存了15分鐘
  • 視圖緩存與URL無關,若是多個URL指向同一視圖,每一個URL將會分別緩存

模板片段緩存

  • 使用cache模板標籤來緩存模板的一個片斷
  • 須要兩個參數:
    • 緩存時間,以秒爲單位
    • 給緩存片斷起的名稱
  • 示例代碼以下:
{% load cache %}
{% cache 500 hello %}
hello1
<!--hello2-->
{% endcache %}

底層的緩存API

from django.core.cache import cache

設置:cache.set(鍵,值,有效時間)
獲取:cache.get(鍵)
刪除:cache.delete(鍵)
清空:cache.clear()

全文檢索

  • 全文檢索不一樣於特定字段的模糊查詢,使用全文檢索的效率更高,而且可以對於中文進行分詞處理
  • haystack:django的一個包,能夠方便地對model裏面的內容進行索引、搜索,設計爲支持whoosh,solr,Xapian,Elasticsearc四種全文檢索引擎後端,屬於一種全文檢索的框架
  • whoosh:純Python編寫的全文搜索引擎,雖然性能比不上sphinx、xapian、Elasticsearc等,可是無二進制包,程序不會莫名其妙的崩潰,對於小型的站點,whoosh已經足夠使用
  • jieba:一款免費的中文分詞包,若是以爲很差用可使用一些收費產品

操做

1.在虛擬環境中依次安裝包

pip install django-haystack
pip install whoosh
pip install jieba

2.修改settings.py文件

  • 添加應用
INSTALLED_APPS = (
    ...
    'haystack',
)
  • 添加搜索引擎
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
    }
}

#自動生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

3.在項目的urls.py中添加url

urlpatterns = [
    ...
    url(r'^search/', include('haystack.urls')),
]

4.在應用目錄下創建search_indexes.py文件

# coding=utf-8
from haystack import indexes
from models import GoodsInfo


class GoodsInfoIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)

    def get_model(self):
        return GoodsInfo

    def index_queryset(self, using=None):
        return self.get_model().objects.all()

5.在目錄「templates/search/indexes/應用名稱/」下建立「模型類名稱_text.txt」文件

#goodsinfo_text.txt,這裏列出了要對哪些列的內容進行檢索
{{ object.gName }}
{{ object.gSubName }}
{{ object.gDes }}

6.在目錄「templates/search/」下創建search.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
{% if query %}
    <h3>搜索結果以下:</h3>
    {% for result in page.object_list %}
        <a href="/{{ result.object.id }}/">{{ result.object.gName }}</a><br/>
    {% empty %}
        <p>啥也沒找到</p>
    {% endfor %}

    {% if page.has_previous or page.has_next %}
        <div>
            {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; 上一頁{% if page.has_previous %}</a>{% endif %}
        |
            {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}下一頁 &raquo;{% if page.has_next %}</a>{% endif %}
        </div>
    {% endif %}
{% endif %}
</body>
</html>

7.創建ChineseAnalyzer.py文件

  • 保存在haystack的安裝文件夾下,路徑如「/home/python/.virtualenvs/django_py2/lib/python2.7/site-packages/haystack/backends」
import jieba
from whoosh.analysis import Tokenizer, Token


class ChineseTokenizer(Tokenizer):
    def __call__(self, value, positions=False, chars=False,
                 keeporiginal=False, removestops=True,
                 start_pos=0, start_char=0, mode='', **kwargs):
        t = Token(positions, chars, removestops=removestops, mode=mode,
                  **kwargs)
        seglist = jieba.cut(value, cut_all=True)
        for w in seglist:
            t.original = t.text = w
            t.boost = 1.0
            if positions:
                t.pos = start_pos + value.find(w)
            if chars:
                t.startchar = start_char + value.find(w)
                t.endchar = start_char + value.find(w) + len(w)
            yield t


def ChineseAnalyzer():
    return ChineseTokenizer()

8.複製whoosh_backend.py文件,更名爲whoosh_cn_backend.py

  • 注意:複製出來的文件名,末尾會有一個空格,記得要刪除這個空格
from .ChineseAnalyzer import ChineseAnalyzer 
查找
analyzer=StemmingAnalyzer()
改成
analyzer=ChineseAnalyzer()

9.生成索引

  • 初始化索引數據
python manage.py rebuild_index

10.在模板中建立搜索欄

<form method='get' action="/search/" target="_blank">
    <input type="text" name="q">
    <input type="submit" value="查詢">
</form>

 

celery

  • 官方網站
  • 中文文檔
  • 示例一:用戶發起request,並等待response返回。在本些views中,可能須要執行一段耗時的程序,那麼用戶就會等待很長時間,形成很差的用戶體驗
  • 示例二:網站每小時須要同步一次天氣預報信息,可是http是請求觸發的,難道要一小時請求一次嗎?
  • 使用celery後,狀況就不同了
  • 示例一的解決:將耗時的程序放到celery中執行
  • 示例二的解決:使用celery定時執行

名詞

  • 任務task:就是一個Python函數
  • 隊列queue:將須要執行的任務加入到隊列中
  • 工人worker:在一個新進程中,負責執行隊列中的任務
  • 代理人broker:負責調度,在佈置環境中使用redis

使用

  • 安裝包
celery==3.1.25
celery-with-redis==3.0
django-celery==3.1.17
  • 配置settings
INSTALLED_APPS = (
  ...
  'djcelery',
}

...

import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://127.0.0.1:6379/0'
CELERY_IMPORTS = ('應用名稱.task')
  • 在應用目錄下建立task.py文件
import time
from celery import task

@task
def sayhello():
    print('hello ...')
    time.sleep(2)
    print('world ...')
  • 遷移,生成celery須要的數據表
python manage.py migrate
  • 啓動Redis
sudo redis-server /etc/redis/redis.conf
  • 啓動worker
python manage.py celery worker --loglevel=info
  • 調用語法
function.delay(parameters)
  • 使用代碼
#from task import *

def sayhello(request):
    print('hello ...')
    import time
    time.sleep(10)
    print('world ...')

    # sayhello.delay()

    return HttpResponse("hello world")

佈署

  • 從uwsgi、nginx、靜態文件三個方面處理

服務器介紹

  • 服務器:私有服務器、公有服務器
  • 私有服務器:公司本身購買、本身維護,只佈署本身的應用,可供公司內部或外網訪問
  • 公有服務器:集成好運營環境,銷售空間或主機,供其佈署本身的應用
  • 私有服務器成本高,須要專業人員維護,適合大公司使用
  • 公有服務器適合初創公司使用,成本低
  • 經常使用的公有服務器,如阿里雲、青雲等,可根據須要,按流量收費或按時間收費
  • 此處的服務器是物理上的一臺很是高、線路全、運行穩定的機器

服務器環境配置

  • 在本地的虛擬環境中,項目根目錄下,執行命令收集全部包
pip freeze > plist.txt
  • 經過ftp軟件將開發好的項目上傳到此服務器的某個目錄
  • 安裝並建立虛擬環境,若是已有則跳過此步
sudo apt-get install python-virtualenv sudo easy_install virtualenvwrapper mkvirtualenv [虛擬環境名稱]
  • 在虛擬環境上工做,安裝全部須要的包
workon [虛擬環境名稱] pip install -r plist.txt
  • 更改settings.py文件
DEBUG = False ALLOW_HOSTS=['*',]表示能夠訪問服務器的ip
  • 啓動服務器,運行正常,可是靜態文件沒法加載

WSGI

  • python manage.py runserver:這是一款適合開發階段使用的服務器,不適合運行在真實的生產環境中
  • 在生產環境中使用WSGI
  • WSGI:Web服務器網關接口,英文爲Python Web Server Gateway Interface,縮寫爲WSGI,是Python應用程序或框架和Web服務器之間的一種接口,被普遍接受
  • WSGI沒有官方的實現, 由於WSGI更像一個協議,只要遵守這些協議,WSGI應用(Application)均可以在任何服務器(Server)上運行
  • 命令django-admin startproject會生成一個簡單的wsgi.py文件,肯定了settings、application對象
    • application對象:在Python模塊中使用application對象與應用服務器交互
    • settings模塊:Django須要導入settings模塊,這裏是應用定義的地方
  • 此處的服務器是一個軟件,能夠監聽網卡端口、聽從網絡層傳輸協議,收發http協議級別的數據

uWSGI

  • uWSGI實現了WSGI的全部接口,是一個快速、自我修復、開發人員和系統管理員友好的服務器
  • uWSGI代碼徹底用C編寫
  • 安裝uWSGI
pip install uwsgi
  • 配置uWSGI,在項目中新建文件uwsgi.ini,編寫以下配置
[uwsgi] socket=外網ip:端口(使用nginx鏈接時,使用socket) http=外網ip:端口(直接作web服務器,使用http) chdir=項目根目錄 wsgi-file=項目中wsgi.py文件的目錄,相對於項目根目錄 processes=4 threads=2 master=True pidfile=uwsgi.pid daemonize=uswgi.log
  • 啓動:uwsgi --ini uwsgi.ini
  • 中止:uwsgi --stop uwsgi.pid
  • 重啓:uwsgi --reload uwsgi.pid
  • 使用http協議查看網站運行狀況,運行正常,可是靜態文件沒法加載

nginx

  • 使用nginx的做用
    • 負載均衡:多臺服務器輪流處理請求
    • 反射代理:隱藏真實服務器
  • 實現構架:客戶端請求nginx,再由nginx請求uwsgi,運行django框架下的python代碼
  • nginx+uwsgi也能夠用於其它框架的python web代碼,不限於django
  • 到官網下載nginx壓縮文件或經過命令安裝
sudo apt-get nginx
  • 這裏如下載壓縮文件爲例演示
解壓縮: tar zxvf nginx-1.6.3.tar.gz 進入nginx-1.6.3目錄依次執行以下命令進行安裝: ./configure make sudo make install
  • 默認安裝到/usr/local/nginx目錄,進入此目錄執行命令
  • 查看版本:sudo sbin/nginx -v
  • 啓動:sudo sbin/nginx
  • 中止:sudo sbin/nginx -s stop
  • 重啓:sudo sbin/nginx -s reload
  • 經過瀏覽器查看nginx運行結果
  • 指向uwsgi項目:編輯conf/nginx.conf文件
sudo conf/nginx.conf 在server下添加新的location項,指向uwsgi的ip與端口 location / { include uwsgi_params;將全部的參數轉到uwsgi下 uwsgi_pass uwsgi的ip與端口; }
  • 修改uwsgi.ini文件,啓動socket,禁用http
  • 重啓nginx、uwsgi
  • 在瀏覽器中查看項目,發現靜態文件加載不正常,接下來解決靜態文件的問題

靜態文件

  • 靜態文件一直都找不到,如今終於能夠解決了
  • 全部的靜態文件都會由nginx處理,不會將請求轉到uwsgi
  • 配置nginx的靜態項,打開conf/nginx.conf文件,找到server,添加新location
location /static { alias /var/www/test5/static/; }
  • 在服務器上建立目錄結構「/var/www/test5/」
  • 修改目錄權限
sudo chmod 777 /var/www/test5
  • 建立static目錄,注意順序是先分配權限,再建立目錄
mkdir static
  • 最終目錄結構以下圖:

 

  • 修改settings.py文件
STATIC_ROOT='/var/www/test5/static/'
STATIC_URL='/static/'
  • 收集全部靜態文件到static_root指定目錄:python manage.py collectstatic
  • 重啓nginx、uwsgi

 

總結

  • 富文本編輯器
  • 緩存
  • 全文檢索
  • celery
  • 佈署

做業

  • 熟練完成當天代碼
  • 完成佈置

 

Git與項目

  • git的使用,主要包括:
    • 本地倉庫的命令
    • 遠程倉庫的命令
  • 項目需求、頁面、模型類的設計,及頁面的使用

Git簡介

  • Git是目前世界上最早進的分佈式版本控制系統
  • 安裝
sudo apt-get install git
  • 安裝成功後,運行以下命令
git

產生

  • Linus在1991年建立了開源的Linux,今後,Linux系統不斷髮展,已經成爲最大的服務器系統軟件了。Linus雖然建立了Linux,但Linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地爲Linux編寫代碼,那Linux的代碼是如何管理的呢?事實是,在2002年之前,世界各地的志願者把源代碼文件經過diff的方式發給Linus,而後由Linus本人經過手工方式合併代碼!你也許會想,爲何Linus不把Linux代碼放到版本控制系統裏呢?不是有CVS、SVN這些免費的版本控制系統嗎?由於Linus堅決地反對CVS和SVN,這些集中式的版本控制系統不但速度慢,並且必須聯網才能使用。有一些商用的版本控制系統,雖然比CVS、SVN好用,但那是付費的,和Linux的開源精神不符。不過,到了2002年,Linux系統已經發展了十年了,代碼庫之大讓Linus很難繼續經過手工方式管理了,社區的弟兄們也對這種方式表達了強烈不滿,因而Linus選擇了一個商業的版本控制系統BitKeeper,BitKeeper的東家BitMover公司出於人道主義精神,受權Linux社區無償使用這個版本控制系統。安定團結的大好局面在2005年就被打破了,緣由是Linux社區牛人彙集,難免沾染了一些梁山好漢的江湖習氣。開發Samba的Andrew試圖破解BitKeeper的協議(這麼幹的其實也不僅他一個),被BitMover公司發現了(監控工做作得不錯!),因而BitMover公司怒了,要收回Linux社區的無償使用權。Linus能夠向BitMover公司道個歉,保證之後嚴格管教弟兄們,嗯,這是不可能的。實際狀況是這樣的:Linus花了兩週時間本身用C寫了一個分佈式版本控制系統,這就是Git!一個月以內,Linux系統的源碼已經由Git管理了!牛是怎麼定義的呢?你們能夠體會一下。Git迅速成爲最流行的分佈式版本控制系統,尤爲是2008年,GitHub網站上線了,它爲開源項目免費提供Git存儲,無數開源項目開始遷移至GitHub,包括jQuery,PHP,Ruby等等。歷史就是這麼偶然,若是不是當年BitMover公司威脅Linux社區,可能如今咱們就沒有免費而超級好用的Git了。

 

遠程倉庫

  • Git是分佈式版本控制系統,同一個Git倉庫,能夠分佈到不一樣的機器上。首先找一臺電腦充當服務器的角色,天天24小時開機,其餘每一個人都從這個「服務器」倉庫克隆一份到本身的電腦上,而且各自把各自的提交推送到服務器倉庫裏,也從服務器倉庫中拉取別人的提交。能夠本身搭建這臺服務器,也可使用GitHub網站

建立github帳號

  • 本地Git倉庫和GitHub倉庫之間的傳輸是經過SSH加密的
  • step1:建立項目的SSH Key
ssh-keygen -t rsa -C "youremail@example.com"
  • 建立完成後,在用戶主目錄裏找到.ssh目錄,裏面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH Key的祕鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,能夠放心地告訴任何人
  • step2:* 登陸github註冊或登陸帳號,打開「settings」的「SSH Keys」頁面,而後,點「New SSH Key」,填上任意Title,在Key文本框裏粘貼id_rsa.pub文件的內容,點「Add Key」,你就應該看到已經添加的Key

建立遠程庫

  • 登錄GitHub
  • 在右上角找到「new repository」按鈕,建立一個新的倉庫

從遠程庫克隆

  • 將github上的項目,克隆到本地一份
git clone git@github.com:帳號名/項目名.git

與遠程庫交互

  • 從遠程庫獲取到本地
git pull
  • 將本地提交遠程庫
git push origin master
  • 提示:每次提交前,須要先獲取,解決衝突後再次提交

本地倉庫

建立本地倉庫

  • 建立空目錄
mkdir test7
cd test7
  • 目錄結構圖以下:

 

  • 在目錄下建立本地倉庫
git init
  • 建立成功後,目錄結構以下圖:

 

  • 版本庫就是一個目錄,這個目錄裏面的全部文件均可以被Git管理起來,每一個文件的修改、刪除,Git都能跟蹤,以便任什麼時候刻均可以追蹤歷史,或者在未來某個時刻能夠「還原」

文件管理

  • 本地倉庫分爲三部分:工做區,暫存區,倉庫區,其中暫存區、倉庫區是版本庫部分
  • 三個部分的操做及命令以下圖

 

工做區與暫存區

  • 使用IDE打開目錄,建立項目,將文件添加到暫存區
git add 文件1 文件2 ...
git add 目錄
  • 使用暫時區的內容恢復工做區的內容
git checkout -- 文件名
  • 查看暫存區的狀態
git status

暫存區與倉庫區

  • 將暫存區的記錄添加到倉庫區
git commit -m '本次提交的說明信息'
  • 查看倉庫區的歷史
當前版本的歷史版本:git log
簡版顯示:git log --pretty=oneline
歷史命令:git reflog
  • 在Git中,用HEAD表示當前版本,也就是最新的提交3628164...882e1e0(注意個人提交ID和你的確定不同),上一個版本就是HEAD^,上上一個版本就是HEAD^^,固然往上100個版本寫100個^比較容易數不過來,因此寫成HEAD~100

  • 對比工做區和倉庫區中某版本某文件的不一樣

git diff HEAD -- 文件名
  • 回退歷史版本到暫存區
git reset HEAD^或版本號

刪除文件

  • 依次執行以下命令
rm 文件名
git rm 文件名
git commit -m '說明信息'

 

項目設計

  • 這是一個垂直電商,只針對生鮮水果的一個電商平臺
  • 下發頁面
  • 實現功能:首頁、列表頁、展現頁、購物車、訂單、用戶登陸註冊、用戶中心
  • 分組完成開發,一組人員爲4-5人

模型類設計

  • 用戶UserInfo
    • uname
    • upwd
    • ...
    • isDelete
  • 商品分類TypeInfo
    • ttitle
    • isDelete
  • 商品GoodsInfo
    • gtitle
    • gtype
    • gprice
    • gdesc
    • isDelete
  • 購物車CartInfo
    • user
    • goods
    • count
  • 訂單OrderInfo
    • user
    • ototal
    • state
  • 訂單詳細OrderDetailInfo
    • order
    • goods
    • count
    • price

 

使用頁面

  • 將現有頁面進行整理,劃分出template、static目錄下的內容
  • 找出模板頁面的繼承關係
  • 替換模板中的數據

總結

  • git的使用,主要包括:
    • 本地倉庫的命令
    • 遠程倉庫的命令
  • 項目需求、頁面、模型類的設計,及頁面的使用

做業

  • 按組完成項目開發,並在最後一天的下午,完成項目路演
相關文章
相關標籤/搜索